Назначение
Выполнение манипуляций с базой данных MySQLИнсталляция
Команда является частью пакета дополнений asterisk-addons, который доступен в Asterisk CVS. Это ДОПОЛНЕНИЕ к asterisk, данный пакет не устанавливается по умолчанию и должен быть скачан и установлен из пакета дополнений asterisk-addons.Описание
MYSQL(): Выполнение манипуляций с базой данных MySQLСинтаксис
MYSQL(Connect connid dhhost dbuser dbpass dbname)Соединение с базой данных. В аргументах содержаться стандартные параметры для соединения с базой MySQL, которые будут переданы функции mysql_real_connect. Идентификатор соединения будет возвращен в переменной ${connid}
MYSQL(Query resultid ${connid} query-string)
Выполняет стандартный запрос к базе данных MySQL, запрос содержится в параметре query-string, используется идентификатор соединения определенный в ${connid}. Результат запроса будет сохранен в переменной ${resultid}.
MYSQL(Fetch fetchid ${resultid} var1 var2 ... varN)
Если есть доступные для выборки записи в результате запроса к базе данных, ${fetchid} устанавливается в 1 и одна запись будет выбрана из возвращенного результата, хранящегося в ${resultid}. Полученные значения полей будут назначены переменным ${var1}, ${var2} ... ${varN} согласно указанному в запросе порядку.
Если нет доступных записей, то переменная ${fetchid} будет сброшена в 0 и ${var1}, ${var2} ... ${varN} будут возвращены без изменений.
MYSQL(Clear ${resultid})
Очищает память и структуры данных связанных с результатом запроса.
MYSQL(Disconnect ${connid})
Прекращает соединение с базой MySQL с данным идентификатором.
Примеры
exten => _X.,1,MYSQL(Connect connid localhost dbuser dbpass dbname)
exten => _X.,2,MYSQL(Query resultid ${connid} SELECT\ scriptname\ from\ mac2pin\ where\ userid=${CALLERIDNAME})
exten => _X.,3,MYSQL(Fetch fetchid ${resultid} AGIScript)
exten => _X.,4,GotoIf($[${AGIScript} = NULL]?5:7)
exten => _X.,5,AGI(${DefaultAGIScript},${EXTEN})
exten => _X.,6,Goto(_X.,8)
exten => _X.,7,AGI(${AGIScript},${EXTEN})
exten => _X.,8,MYSQL(Clear ${resultid})
exten => _X.,9,MYSQL(Disconnect ${connid})
exten => _X.,10,Hangup
exten => _X.,2,MYSQL(Query resultid ${connid} SELECT\ scriptname\ from\ mac2pin\ where\ userid=${CALLERIDNAME})
exten => _X.,3,MYSQL(Fetch fetchid ${resultid} AGIScript)
exten => _X.,4,GotoIf($[${AGIScript} = NULL]?5:7)
exten => _X.,5,AGI(${DefaultAGIScript},${EXTEN})
exten => _X.,6,Goto(_X.,8)
exten => _X.,7,AGI(${AGIScript},${EXTEN})
exten => _X.,8,MYSQL(Clear ${resultid})
exten => _X.,9,MYSQL(Disconnect ${connid})
exten => _X.,10,Hangup
В вышеприведенном примере, если звонящий пользователь отсоединится, когда его вызов обрабатывается, где-то в промежутке между 5 и 7 приоритетом, тогда функции MYSQL(Clear...) и MYSQL(Disconnect....) не будут выполнены. В этом случае соединение останется не закрытым, и с каждым новым таким вызовом число незакрытых соединений будет увеличиваться. В конечном счете, в MySQL сервере кончится лимит на количество одновременно открытых соединений с базой. (В зависимости от установленного лимита в конфигурационном файле mysql). Поэтому, для данного случая нужно немного изменить последовательность действий так, как показано ниже:
exten => _X.,1,MYSQL(Connect connid localhost asterisk dbpass asterisk)
exten => _X.,2,MYSQL(Query resultid ${connid} SELECT\ scriptname\ from\ mac2pin\ where\ userid=${CALLERIDNAME})
exten => _X.,3,MYSQL(Fetch fetchid ${resultid} AGIScript)
exten => _X.,4,MYSQL(Clear ${resultid})
exten => _X.,5,MYSQL(Disconnect ${connid})
exten => _X.,6,GotoIf(${fetchid}?7:9)
exten => _X.,7,AGI(${DefaultAGIScript},${EXTEN})
exten => _X.,8,Hangup
exten => _X.,9,AGI(${AGIScript},${EXTEN})
exten => _X.,10,Hangup
exten => _X.,2,MYSQL(Query resultid ${connid} SELECT\ scriptname\ from\ mac2pin\ where\ userid=${CALLERIDNAME})
exten => _X.,3,MYSQL(Fetch fetchid ${resultid} AGIScript)
exten => _X.,4,MYSQL(Clear ${resultid})
exten => _X.,5,MYSQL(Disconnect ${connid})
exten => _X.,6,GotoIf(${fetchid}?7:9)
exten => _X.,7,AGI(${DefaultAGIScript},${EXTEN})
exten => _X.,8,Hangup
exten => _X.,9,AGI(${AGIScript},${EXTEN})
exten => _X.,10,Hangup
Замечание для данного случая. Тут мы очищаем память и отсоединяемся от базы данных сразу же после Fetch. Это гарантирует то, что перед тем как мы запустим на выполнение AGI скрипт, мы будем уверены, что Очистка памяти и отсоединение от базы данных уже было выполнено. Этот метод можно применять, если Вам действительно необходимо получить из базы что-то либо уникальное, либо часто изменяющееся.
Другие моменты, которые необходимо учитывать:
- Экранирование пробелов с помощью символа \ при составлении запроса. Если Вы используете кавычки, то они будут отправлены в приложение, как часть запроса.
- Другие символы, для которых необходимо экранирование - это кавычки (\' и \"), запятая (\,) и обратный слеш (\\). (Используйте Mysqlscape - смотри нижеприведенный пример)
- Возвращаемые запросом поля, будут назначены переменным в том же порядке, в каком они были возвращены MySQL. Не рекомендуется использовать запрос типа "SELECT *", потому что нет гарантии того, что возвращаемые поля будут в том порядке, в котором Вы их ожидаете. Вам нет необходимости использовать псевдонимы для выбираемых из базы полей типа: "SELECT (длинное выражение) as короткое_имя", потому что имя поля не влияет на порядок полей, полученных по этому запросу.
- В описании asterisk для этой команды значится, что ${fetchid} устанавливается в TRUE, если есть доступные записи. Это неправильно. Оно устанавливается в 1, если есть данные после выполнения последней операции fetch и устанавливается в 0, если нет.
Пример
exten => 888,1,MYSQL(Connect connid localhost ipcontact passwd ipcontact)
exten => 888,2,MYSQL(Query resultid ${connid} SELECT\ `number`\ FROM\ `phones`\ WHERE\ `channel`=\'${chan}\')
exten => 888,3,MYSQL(Fetch foundRow ${resultid} number) ; fetch row
exten => 888,4,GotoIf($["${foundRow}" = "1"]?7:5) ; leave loop if no row found
exten => 888,5,NoOp(${number})
exten => 888,6,Goto(3) ; continue loop if row found
exten => 888,7,MYSQL(Clear ${resultid})
exten => 888,8,MYSQL(Disconnect ${connid})
exten => 888,2,MYSQL(Query resultid ${connid} SELECT\ `number`\ FROM\ `phones`\ WHERE\ `channel`=\'${chan}\')
exten => 888,3,MYSQL(Fetch foundRow ${resultid} number) ; fetch row
exten => 888,4,GotoIf($["${foundRow}" = "1"]?7:5) ; leave loop if no row found
exten => 888,5,NoOp(${number})
exten => 888,6,Goto(3) ; continue loop if row found
exten => 888,7,MYSQL(Clear ${resultid})
exten => 888,8,MYSQL(Disconnect ${connid})
Примечания
- __Обратите внимание на поправку с MYSQL(Fetch). ${fetchid} - не обязательно устанавливается в 1, если есть доступные данные, Эта функция устанавливает это поле в 1, если есть данные только для этого конкретного вызова MYSQL(Fetch). Это было изначально неправильно объяснено, еще с создания описания данной функции.
- Если Вы не очистите память и не отсоединитесь от MySQL, соединения останутся открытыми и в конечном итоге их количество достигнет лимита, установленного для MySQL сервера. Тогда, единственный путь исправить эту ситуацию - это перезагрузка Asterisk, или еще можно посмотреть список процессов и прибить эти висящие процессы командой kill.
- В дополнение к вышесказанному, Flobi придумал некое извращение для очистки ресурсов. Так как я не уловил логику его действий и нашел ее полным бредом, то переводить я это не стал.
Ссылки по теме:
- MySQL
- Asterisk db
- Mysqlscape - Утилита автоматического экранирования специальных символов mysql для команды Asterisk - MYSQL.