08 мая 2010

PostgeSQL записки #2 для QT Script

Еще один способ подписаться на RAISE [level] ... записки генерируемые на стороне PostgreSQL сервера. На этот раз я просто добавил динамическую загрузку libpq.

Вытаскиваем необходимую информацию из заголовочного файла libpq-fe.h

#define PG_DIAG_SEVERITY  'S'
#define PG_DIAG_SQLSTATE  'C'
#define PG_DIAG_MESSAGE_PRIMARY 'M'
#define PG_DIAG_MESSAGE_DETAIL 'D'
#define PG_DIAG_MESSAGE_HINT 'H'
#define PG_DIAG_STATEMENT_POSITION 'P'
#define PG_DIAG_INTERNAL_POSITION 'p'
#define PG_DIAG_INTERNAL_QUERY 'q'
#define PG_DIAG_CONTEXT   'W'
#define PG_DIAG_SOURCE_FILE  'F'
#define PG_DIAG_SOURCE_LINE  'L'
#define PG_DIAG_SOURCE_FUNCTION 'R'

typedef void (*PQnoticeReceiver) (void *arg, const PGresult *res);

typedef PQnoticeReceiver (*PQsetNoticeReceiver)(PGconn *conn,
PQnoticeReceiver proc,
void *arg);

typedef char * (*PQresultErrorField)(const PGresult *res, int fieldcode);

PQsetNoticeReceiver pqSetNoticeReceiver;
PQresultErrorField pqResultErrorField;
Подключаем qt заголовочные файлы:
#include 
#include 
#include 
Создаем функцию, которая динамически загрузит библиотеку libpq.
bool initPostgresqlLibrary()
{
  QLibrary library("libpq");
  if (library.load()) {
    pqSetNoticeReceiver = (PQsetNoticeReceiver)(library.resolve("PQsetNoticeReceiver"));
    pqResultErrorField = (PQresultErrorField)(library.resolve("PQresultErrorField"));
    return pqSetNoticeReceiver && pqResultErrorField;
  }
  return false;
}

Создаем функцию, которая будет непосредственно принимать записки от PostgreSQL сервера и отсылать данные записки функции определенной в qt script. Кроме функции также определяем статическую переменную, которая будет хранить "ссылку" на qt script функцию.
QScriptValue scriptNoticeReceiver = QScriptValue();

// MINI HACK
void PostgreSQLnoticeReceiver(void *arg ,const PGresult *res)
{
  QString severity = QString::fromUtf8(pqResultErrorField(res, PG_DIAG_SEVERITY));
  QString primary = QString::fromUtf8(pqResultErrorField(res, PG_DIAG_MESSAGE_PRIMARY));
  QString detail = QString::fromUtf8(pqResultErrorField(res, PG_DIAG_MESSAGE_DETAIL));
  QString hint = QString::fromUtf8(pqResultErrorField(res, PG_DIAG_MESSAGE_HINT));

  if (scriptNoticeReceiver.isFunction()) {
    QScriptValueList arguments;
    arguments << severity << primary << detail << hint;
    scriptNoticeReceiver.call(scriptNoticeReceiver.engine()->globalObject(), arguments);
  }
}

Создаем функцию обертку для qt script, которая будет выполнять регистрацию функции-подписчика на PostgreSQL записки:
// PQsetNoticeProcessor(sqlConnectionName, function)
QScriptValue PQsetNoticeReceiverWrapper(QScriptContext* context, QScriptEngine* /*engine*/)
{
  if (context->argumentCount() == 2) {
    QString connectionName = context->argument(0).toString();
    if (QSqlDatabase::contains(connectionName) && context->argument(1).isFunction()) {
      QVariant driverHandle = QSqlDatabase::database(connectionName).driver()->handle();
      if (!QString::compare(driverHandle.typeName(),"PGconn*")) {
        PGconn *handle = *static_cast(driverHandle.data());
        if (handle != 0) {
          scriptNoticeReceiver = context->argument(1);
          if (initPostgresqlLibrary()) {
            pqSetNoticeReceiver(handle, PostgreSQLnoticeReceiver, 0);
            return true;
          }
        }
      }
    }
  }
  return false;
}

Где-то в инициализации системы qt сценариев:
QScriptEngine *engine = new QScriptEngine;
....................
engine->globalObject().setProperty("PQsetNoticeReceiver", engine->newFunction(PQsetNoticeReceiverWrapper));
В qt script оболочки появиться свойство-функция глобального объекта PQsetNoticeReceiver(function), которая принимает в качестве агрумента функцию. Пример использования:
// Создаем подписчика на PostgreSQL записки.
// static function
noticeReceiver = function(severity, primary, detail, hint) {
  message = primary;
  if (detail != "")
    message += "\n" + detail;
  if (hint != "")
    message += "\n" + detail;
  if (severity == "WARNING") {
    alert(message);
  } else if (severity == "NOTICE") {
    print(message);
  } else if (severity == "INFO") {
    print(message);
  } else if (severity == "LOG") {
    print(message);
  }
};
// Подписываемся на PostgreSQL записки
if (!PQsetNoticeReceiver(connectionName, this.noticeReceiver))
  print("Unable to set postgresql notice receiver");

Комментариев нет:

Отправить комментарий