[SourcePawn] Урок 13 - Работа с базами данных (MySQL, SQLite)

R1KO

fuck society
Сообщения
9,457
Реакции
7,786
  • Команда форума
  • #61
SQL_ExecuteTransaction выполняется в отдельном потоке?
Или её лучше заменить на несколько SQL_TQuery?
в отдельном
И еще вопрос:
Database.Connect - это тоже самое что и SQL_TConnect в плане поточности?
да, это просто "ооп" аналог
 

Kruzya

Участник
Сообщения
12,970
Реакции
10,914
  • Команда форума
  • #63
А дампаните итоговый запрос на вставку, да покажите.
 

Dragokas

Добрая душа
Сообщения
229
Реакции
213
Думаю, в раздел о "Транзакциях" нужно хотя бы какую-то пометку сделать
о том, что для них важен выбор типа базы данных (Transaction-Safe).

Сейчас вот читаю об этом, одни источники пишут, что в MyISAM транзакции не поддерживаются,
в других, что просто не будет работать такая фича как "откат", в случаях когда:
- выключили питание
- крашнулся драйвер
- часть запросов прошли неудачно.

А у меня как раз такой тип базы.

Такой вопрос:
Допустим, мне вышеперечисленное сейчас некритично,
зато важно чтобы операция была атомарна со стороны sm,
т.е. на нее не влиял обрыв сетевого подключения.
Если у меня MyISAM и я отправляю транзакцию через SourceMod,
она ведь уходит на удалённый сервер одним пакетом данных
и не начнёт выполняться, пока БД не примет запрос целиком,
даже если тип базы не является Transaction-Safe?
 

R1KO

fuck society
Сообщения
9,457
Реакции
7,786
  • Команда форума
  • #65
Сейчас вот читаю об этом, одни источники пишут, что в MyISAM транзакции не поддерживаются,
Так и есть

Такой вопрос:
Допустим, мне вышеперечисленное сейчас некритично,
зато важно чтобы операция была атомарна со стороны sm,
т.е. на нее не влиял обрыв сетевого подключения.
Если у меня MyISAM и я отправляю транзакцию через SourceMod,
она ведь уходит на удалённый сервер одним пакетом данных
и не начнёт выполняться, пока БД не примет запрос целиком,
даже если тип базы не является Transaction-Safe?

см об этом ничего не знает.
Он шлет в субд команду начала транзакции, потом запросы, потом коммит
alliedmodders/sourcemod
Если субд этого не поддерживает то надо смотреть проигнорит она команды управления транзакцией или выплюнет ошибку.
 

Dragokas

Добрая душа
Сообщения
229
Реакции
213
У меня не игнорит. MySQL MyISAM у меня нормально выполняет такие запросы от sm.

Он шлет в субд команду начала транзакции, потом запросы, потом коммит
Спасибо за ссылочку. Не обнадеживает :)
Попробую напрямую скормить такой базе и посмотреть,
в какой момент запросы начнут выполняться, сразу или только после команды "COMMIT".
Сообщения автоматически склеены:

Покурил ману, посмотрел еще раз на устрашающее слово Exec, исполняемое по циклу,
и понял, что всё это добро будет исполняться тут же на месте отдельно для каждого запроса,
что и объясняет проблему с частичным коммитом, на которую я уже нарвался.

Можно еще такой нубский вопрос:
Вот везде пишут - чтобы это нормально работало нужно в InnoDB исполнить SET AUTOCOMMIT=0,
тогда запросы не будут модифицировать таблицу мгновенно, (либо начинать запрос со слов START TRANSACTION).
Но sm юзает "BEGIN", следовательно о SET AUTOCOMMIT=0 мне нужно явно позаботится самому.

Но, что будет если этого не сделать? Т.е., если у меня SET AUTOCOMMIT=1 и я делаю запрос с автокоммитом,
я его уже не смогу отменить через команду ROLLBACK ?
 
Последнее редактирование:

R1KO

fuck society
Сообщения
9,457
Реакции
7,786
  • Команда форума
  • #67
Покурил ману, посмотрел еще раз на устрашающее слово Exec, исполняемое по циклу,
и понял, что всё это добро будет исполняться тут же на месте отдельно для каждого запроса,
что и объясняет проблему с частичным коммитом, на которую я уже нарвался.
ну значит субд просто проигнорит команды управления транзакцией и будет выполнять все запросы как атомарные.
НО! при ошибке одного из запросов последующие продолжат выполняться и роллбека не будет (ну точнее команда будет, но субд её проигнорит т.к. не поддерживает, если ты сказал что ошибку не кидает)

Можно еще такой нубский вопрос:
Вот везде пишут - чтобы это нормально работало нужно в InnoDB исполнить SET AUTOCOMMIT=0,
тогда запросы не будут модифицировать таблицу мгновенно, (либо начинать запрос со слов START TRANSACTION).
Но sm юзает "BEGIN", следовательно о SET AUTOCOMMIT=0 мне нужно явно позаботится самому.

Но, что будет если этого не сделать? Т.е., если у меня SET AUTOCOMMIT=1 и я делаю запрос с автокоммитом,
я его уже не смогу отменить через команду ROLLBACK ?
BEGIN это просто более которкий алиас START TRANSACTION

SET AUTOCOMMIT
это уже настройка самой базы и см к ней отношения не имеет, он просто отправляет запросы, а остальным рулит уже сама субд.
Если таблица MyISAM то скорее всего команда ROLLBACK будет игнорится в принципе. И ничего не мешает проверить это ручками
 

DenisPukin

Капитан Костыль
Сообщения
185
Реакции
34
Помогите пожалуйста!
Не пойму в чем проблема.
C++:
public void ConnectMap_DB()
{
    char szQuery[256], szName[MAX_NAME_LENGTH*2+1], szBuff[128];
    GetCurrentMap(szBuff, sizeof(szBuff));
    g_hDatabase.Escape(szBuff, szName, sizeof(szName)); <-- тут ошибка.
    FormatEx(szQuery, sizeof(szQuery), "SELECT `game`, `maxplayers` FROM `mn_map` WHERE `namemap` = '%s';", szName);
    g_hDatabase.Query(SQL_ConnectMap, szQuery);
}
Сама ошибка:
[SM] Exception reported: Invalid database Handle 0 (error: 4)
 

xstage

🏹
Сообщения
726
Реакции
754
Помогите пожалуйста!
Не пойму в чем проблема.
C++:
public void ConnectMap_DB()
{
    char szQuery[256], szName[MAX_NAME_LENGTH*2+1], szBuff[128];
    GetCurrentMap(szBuff, sizeof(szBuff));
    g_hDatabase.Escape(szBuff, szName, sizeof(szName)); <-- тут ошибка.
    FormatEx(szQuery, sizeof(szQuery), "SELECT `game`, `maxplayers` FROM `mn_map` WHERE `namemap` = '%s';", szName);
    g_hDatabase.Query(SQL_ConnectMap, szQuery);
}
Сама ошибка:
[SM] Exception reported: Invalid database Handle 0 (error: 4)
Ты при коннекте передал хендел бд?
 

RudikS

Участник
Сообщения
130
Реакции
61
Помогите пожалуйста!
Не пойму в чем проблема.
C++:
public void ConnectMap_DB()
{
    char szQuery[256], szName[MAX_NAME_LENGTH*2+1], szBuff[128];
    GetCurrentMap(szBuff, sizeof(szBuff));
    g_hDatabase.Escape(szBuff, szName, sizeof(szName)); <-- тут ошибка.
    FormatEx(szQuery, sizeof(szQuery), "SELECT `game`, `maxplayers` FROM `mn_map` WHERE `namemap` = '%s';", szName);
    g_hDatabase.Query(SQL_ConnectMap, szQuery);
}
Сама ошибка:
[SM] Exception reported: Invalid database Handle 0 (error: 4)
код полный скинь
 

DenisPukin

Капитан Костыль
Сообщения
185
Реакции
34
C++:
Database g_hDatabase;
public void OnPluginStart()
{
    Database.Connect(ConnectCallBack, "mnsystem");
}
public void ConnectCallBack(Database hDatabase, const char[] sError, any data)
{
    if (hDatabase == null)
    {
        SetFailState("Database failure: %s", sError);
        return;
    }

    g_hDatabase = hDatabase;
  
    SQL_LockDatabase(g_hDatabase);
                                                          
    g_hDatabase.Query(SQL_CheckError,    "CREATE TABLE IF NOT EXISTS `mn_map` (\
                                                            `namemap` VARCHAR(32) NOT NULL,\
                                                            `game` INTEGER NOT NULL default '0',\
                                                            `maxplayers` INTEGER NOT NULL default '0');");
  
    SQL_UnlockDatabase(g_hDatabase);
  
    g_hDatabase.SetCharset("utf8");
}

public void OnMapStart()
{
    ConnectMap_DB();
}
public void SQL_ConnectMap(Database hDatabase, DBResultSet hResults, const char[] sError, any data)
{
    if(sError[0])
    {
        LogError("SQL_ConnectMap: %s", sError);
        return;
    }
   
    if(hResults.FetchRow())
    {
        g_iGameMap = hResults.FetchInt(0);
        g_iMxPl = hResults.FetchInt(1);
    }
    else
    {
        char szQuery[256], szName[128];
        g_hDatabase.Escape(szMap, szName, sizeof(szName));
        FormatEx(szQuery, sizeof(szQuery), "INSERT INTO `mn_map` (`namemap`) VALUES ( '%s');", szName);
        g_hDatabase.Query(SQL_CreateMap, szQuery);
    }
}
 

RudikS

Участник
Сообщения
130
Реакции
61
C++:
Database g_hDatabase;
public void OnPluginStart()
{
    Database.Connect(ConnectCallBack, "mnsystem");
}
public void ConnectCallBack(Database hDatabase, const char[] sError, any data)
{
    if (hDatabase == null)
    {
        SetFailState("Database failure: %s", sError);
        return;
    }

    g_hDatabase = hDatabase;
 
    SQL_LockDatabase(g_hDatabase);
                                                         
    g_hDatabase.Query(SQL_CheckError,    "CREATE TABLE IF NOT EXISTS `mn_map` (\
                                                            `namemap` VARCHAR(32) NOT NULL,\
                                                            `game` INTEGER NOT NULL default '0',\
                                                            `maxplayers` INTEGER NOT NULL default '0');");
 
    SQL_UnlockDatabase(g_hDatabase);
 
    g_hDatabase.SetCharset("utf8");
}

public void OnMapStart()
{
    ConnectMap_DB();
}
public void SQL_ConnectMap(Database hDatabase, DBResultSet hResults, const char[] sError, any data)
{
    if(sError[0])
    {
        LogError("SQL_ConnectMap: %s", sError);
        return;
    }
  
    if(hResults.FetchRow())
    {
        g_iGameMap = hResults.FetchInt(0);
        g_iMxPl = hResults.FetchInt(1);
    }
    else
    {
        char szQuery[256], szName[128];
        g_hDatabase.Escape(szMap, szName, sizeof(szName));
        FormatEx(szQuery, sizeof(szQuery), "INSERT INTO `mn_map` (`namemap`) VALUES ( '%s');", szName);
        g_hDatabase.Query(SQL_CreateMap, szQuery);
    }
}
ConnectMap_DB() явно не в OnMapStart нужно вызывать. За это время подключение еще может не состояться.
 

Dragokas

Добрая душа
Сообщения
229
Реакции
213
Подскажите, как эффективно выполнить около 100 отдельных запросов?
Справится ли SM с такой транзакцией? Или лучше разбить на несколько...
Может, кто проводил подобные опыты.

Скажем, мне нужно получить поля № 2 (версия), по полям № 1, имена у которых абсолютно разные (это будет название квара).
 

R1KO

fuck society
Сообщения
9,457
Реакции
7,786
  • Команда форума
  • #74
Подскажите, как эффективно выполнить около 100 отдельных запросов?
Справится ли SM с такой транзакцией? Или лучше разбить на несколько...
Может, кто проводил подобные опыты.

Скажем, мне нужно получить поля № 2 (версия), по полям № 1, имена у которых абсолютно разные (это будет название квара).
а можно пример таблиц и запросов?
 

Dragokas

Добрая душа
Сообщения
229
Реакции
213
Таблица:
C-подобный:
#define DATABASE_CREATE_TABLE "\
CREATE TABLE IF NOT EXISTS `"...DATABASE_TABLE..."` (\
 `cvar` TINYTEXT NOT NULL,\
 `ver` TINYTEXT NOT NULL,\
 `url` TINYTEXT NOT NULL,\
 `name` TINYTEXT NOT NULL,\
 `time` int(11) NOT NULL DEFAULT '0',\
 PRIMARY KEY (cvar(64))\
) ENGINE=InnoDB DEFAULT CHARSET=utf8;"

Запрос:
C-подобный:
			FormatEx(g_sQuery1, sizeof(g_sQuery1)
				 , "SELECT \
					cvar, \
					ver \
					FROM `"...DATABASE_TABLE..."` WHERE `cvar` = '%s'", cvar);
			
			g_hDB.Query(SQL_Callback_Update, g_sQuery1);
тоже самое, только ~100 раз в виде 1 транзакции, как мне видится.

Собственно плагин будет выполнять задачу проверки обновлений всех плагинов на юзерских серверах.
У меня будет централизованная БД с актуальной инфой о версиях всех плагинов AM, в идеале бы и hlmod тоже.
 

Вложения

  • table.png
    table.png
    2.4 КБ · Просмотры: 23

Kruzya

Участник
Сообщения
12,970
Реакции
10,914
  • Команда форума
  • #76
А не лучше ли запросом одним достать?
 

Dragokas

Добрая душа
Сообщения
229
Реакции
213
Типа запросить всю БД таблицу целиком?
Ну, вот сейчас прикинул, выходит около 6000 записей. Думаете, имеет смысл?

Конечно, лучше попробовать на практике, но я пока просто проектирую.
 

Kruzya

Участник
Сообщения
12,970
Реакции
10,914
  • Команда форума
  • #78
Нет, не всю.
WHERE cvar IN(“cvarname1”, “cvarname2”, …)
 

Dragokas

Добрая душа
Сообщения
229
Реакции
213
Да, спасибо что напомнил про такой синтаксис. Будет отлично.
А вот, есть представление, какова допустимая максимальная длина строки в таком одиночном запросе?
 

Kruzya

Участник
Сообщения
12,970
Реакции
10,914
  • Команда форума
  • #80
Довольно большая)
Там правда ограничение со стороны самого сервера MySQL может быть, но меньше 4к символов не видел.
 
Сверху Снизу