为什么相同的 SQL 查询在 C++ 程序中无效,但在 BD 浏览器中打印结果

问题描述 投票:0回答:1

我有一个使用 SQLite 版本 3.43.1 的 C++ 程序。 (DB 浏览器版本 3.12.2,其中 SQLite 版本 3.35.5)。我创建一个表并在该表中插入数据。当我想阅读表格时:

SELECT NAME FROM "mytable"
,我收到错误代码 1 和错误消息
no such table: mytable

注意:我看到很多以前的答案都说“你需要指向数据库”。我正在从我写入的同一个数据库中查询,没有错误。我还使用数据库浏览器(一旦我的程序断开连接),并且我使用相同的精确查询并查看我的数据。

我还在程序中使用了以下查询来查看表列表:

SELECT * FROM sqlite_master WHERE name LIKE 'mytable'

这怎么可能?我不知道如何继续。

另请注意:我尝试了 sqlite_exec 和非包装方法(prepare_v2、step、sqlite3_column_text)。此时,我不知道使用回调或 step+column_text 之间的更改有何不同。

数据库的创建是使用常规的 sqlite3_open 完成的。除了文件路径之外没有任何参数。创建表使用默认的

CREATE TABLE mytable (ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, NAME TEXT NOT NULL, VAL TEXT);
。没有别的了。

我正在 Ubuntu 12.3.0(通过 Windows WSL)上使用 g++ 12.3.0 在 sqlite3.h 和 sqlite3.c 文件上进行编译,该文件由 sqlite 网站压缩的 sqlite-amalgamation-343100 提供。

等效编译命令:

gcc -fPIC -lpthread -ldl -lm -c sqlite3.o -o code/sqlite-amalgamation-3430100/sqlite3.c

#pragma once

// Third-Party
#include <sqlite3.h>

// C++ Standard
#include <filesystem>
#include <iostream>
#include <sstream>
#include <string>
#include <tuple>
#include <vector>

class DB {
 public:
  void setFilepath(std::filesystem::path);
  std::filesystem::path getFilepath();
  void connect();
  void close();
  void write(std::string);
  void read(std::string);
 private:
  sqlite3* db;
  std::filesystem::path db_path;
#include "db.h"

static int callback(void* list, int cols, char** dat, char** colname) {
  std::cout << "callback called" << std::endl;

  for (int i = 0; i < cols; i++) {
    printf("%s = %s\n", colname[i], dat[i] ? dat[i] : "NULL");
    std::cout << colname[i] << " = " << dat[i] << std::endl;
  }
  printf("\n");

  return 0;
}

void DB::setFilepath(std::filesystem::path fp) {
  std::string msg = "DB set filepath " + fp.string();
  spdlog::info(msg);

  if (fp.extension() != ".db") {
    fp.replace_extension(".db");
  }

  db_path = fp;

  return;
}

std::filesystem::path DB::getFilepath() {
  return db_path;
}

void DB::connect() {
  int ret_code = sqlite3_open(db_path.string().c_str(), &db);
  //const char* zVfs = "";
  //int ret_code = sqlite3_open_v2(db_path.string().c_str(), &db, 0, zVfs);
  if (ret_code) {
    std::string msg1 = "DB return code error ";
    std::string msg2 = sqlite3_errmsg(db);
    spdlog::error(msg1 + msg2);
    exit(0);
  }
}

void DB::close() {
  sqlite3_close(db);
}

void DB::write(std::string query) {
  connect();
  int ret_code;
  char* pzErrMsg = 0;
  ret_code = sqlite3_exec(db, sql_str.c_str(), callback, pArg, &pzErrMsg);

  if (ret_code != SQLITE_OK) {
    spdlog::error("Execution error");
    sqlite3_free(pzErrMsg);
  }
  close();

  return;
}

voiid DB::read(std::string sql_str) {
  int ret_code = 0;
  sqlite3_stmt* stmt;
  char* zErrMsg = 0;

  connect();
  ret_code = sqlite3_prepare_v2(db, sql_str.c_str(), -1, &stmt, nullptr);
  std::cout << "ret code: " << ret_code << std::endl;

  // I receive the erorr HERE
  // #########################################################
  // exe fail
  // no such table: mytable <--------------- HERE
  // #########################################################
  if (ret_code != SQLITE_OK) {
    std::cout << "exec fail" << std::endl;
    std::cerr << sqlite3_errmsg(db) << '\n';
    sqlite3_free(zErrMsg);
    exit(1);
  }


  do {
    ret_code = sqlite3_step(stmt);
    // #########################################################
    // # PLEASE DO NOT WORRY ABOUT THIS RIGHT NOW              #
    // # THIS IS NOT WHERE THE ERROR OCCURS                    #
    // #########################################################
    std::cout << "ret code loop: " << ret_code << std::endl;
    std::cout << sqlite3_column_text(stmt, 0) << ":";
    std::cout << sqlite3_column_text(stmt, 1) << std::endl;
  } while (ret_code == SQLITE_ROW);

  if (ret_code != SQLITE_DONE) { // 101
    std::cout << "ret_code not done" << std::endl;
    std::cerr << sqlite3_errmsg(db) << '\n';
  }
  
  // close
  sqlite3_finalize(stmt);
  sqlite3_close(db);
}
void createTables(DB proj_db) {
  proj_db.write("CREATE TABLE mytable (\"ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, NAME TEXT NOT NULL, VALUE TEXT\");");
}

void writeDatafromFile(json file, DB proj_db) {
  // read data
  ...

  // write to DB
  proj_db.write("INSERT INTO mytable (NAME,VALUE) \"abc\",\"123\"");
}

void readDatabase(DB proj_db) {
  proj_db.read("SELECT NAME FROM \"mytable\";"); //fail
  proj_db.read("SELECT * FROM sqlite_master WHERE name LIKE \'mytable\';"); // works! but only lists the tables that exist. not the data inside the table (just like DB Browser)

  // write to csv
  ...
}

int main(int argc, char const* argv[]) {
  DB proj_db;
  proj_db = DB();
  std::filesystem::path db_fp; // obtained from arguments, same directory is fine. ends with .db. example: ./mydatabase.db
  proj_db.setFilepath(db_fp);
  
  if (arguments.new()) createTables();
  if (arguments.inputFileExists()) writeDatafromFile();
  if (arguments.outputFlagExists()) readDatabase();

  return 0;
}

bash 终端:

./myprogram --db myproject --new     # I create a blank sql db file called myproject.db; creates empty tables (mytable, myOthertable, etc)
./myprogram --db myproject --in data.json # I write data parsed from a file (equivalent to ~2-3 INSERT mytable sql commands)
./myprogram --db myproject --in data2.json # another set of writes (sometimes to another table)
./myprogram --db myproject --out mystuff.csv # queries tables (SELECT NAME from mytable) <------ ERROR HERE, but DB Browser shows data and my schema

我的实际程序返回读取命令的值。只是不想让这个最小的程序复杂化。只要我能修复错误代码,剩下的我就可以处理。

c++ sqlite
1个回答
0
投票
proj_db.write("CREATE TABLE IF NOT EXISTS mytable (ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, NAME TEXT NOT NULL, VALUE TEXT);"); 
proj_db.write("INSERT INTO mytable (NAME,VALUE) VALUES ('abc','123')"); 
proj_db.read("SELECT NAME, VALUE FROM mytable;");
proj_db.read("SELECT * FROM sqlite_master WHERE name LIKE \'mytable\';"); 

对我有用,所以你的插入和选择语句都是错误的。特别是您的 DB::read 方法尝试读取两个列值,因此您需要一个

select
来返回至少两个文本值。另外你还需要

if (ret_code == SQLITE_ROW) {...}

在 DO 循环中

(并在测试前删除现有数据库一次)

© www.soinside.com 2019 - 2024. All rights reserved.