コード例 #1
0
ファイル: latency_config.c プロジェクト: collectd/collectd
int latency_config(latency_config_t *conf, oconfig_item_t *ci) {
  int status = 0;

  for (int i = 0; i < ci->children_num; i++) {
    oconfig_item_t *child = ci->children + i;

    if (strcasecmp("Percentile", child->key) == 0)
      status = latency_config_add_percentile(conf, child);
    else if (strcasecmp("Bucket", child->key) == 0)
      status = latency_config_add_bucket(conf, child);
    else if (strcasecmp("BucketType", child->key) == 0)
      status = cf_util_get_string(child, &conf->bucket_type);
    else
      P_WARNING("\"%s\" is not a valid option within a \"%s\" block.",
                child->key, ci->key);

    if (status != 0)
      return status;
  }

  if ((status == 0) && (conf->percentile_num == 0) &&
      (conf->buckets_num == 0)) {
    P_ERROR("The \"%s\" block must contain at least one "
            "\"Percentile\" or \"Bucket\" option.",
            ci->key);
    return EINVAL;
  }

  return 0;
}
コード例 #2
0
ファイル: utils_rrdcreate.c プロジェクト: BrandonArp/collectd
static int srrd_create(const char *filename, /* {{{ */
                       unsigned long pdp_step, time_t last_up, int argc,
                       const char **argv) {
  int status;
  char *filename_copy;

  if ((filename == NULL) || (argv == NULL))
    return -EINVAL;

  /* Some versions of librrd don't have the `const' qualifier for the first
   * argument, so we have to copy the pointer here to avoid warnings. It sucks,
   * but what else can we do? :(  -octo */
  filename_copy = strdup(filename);
  if (filename_copy == NULL) {
    ERROR("srrd_create: strdup failed.");
    return -ENOMEM;
  }

  optind = 0; /* bug in librrd? */
  rrd_clear_error();

  status = rrd_create_r(filename_copy, pdp_step, last_up, argc, (void *)argv);

  if (status != 0) {
    P_WARNING("srrd_create: rrd_create_r (%s) failed: %s", filename,
              rrd_get_error());
  }

  sfree(filename_copy);

  return status;
} /* }}} int srrd_create */
コード例 #3
0
ファイル: utils_rrdcreate.c プロジェクト: BrandonArp/collectd
/* #endif HAVE_THREADSAFE_LIBRRD */

#else  /* !HAVE_THREADSAFE_LIBRRD */
static int srrd_create(const char *filename, /* {{{ */
                       unsigned long pdp_step, time_t last_up, int argc,
                       const char **argv) {
  int status;

  int new_argc;
  char **new_argv;

  char pdp_step_str[16];
  char last_up_str[16];

  new_argc = 6 + argc;
  new_argv = malloc((new_argc + 1) * sizeof(*new_argv));
  if (new_argv == NULL) {
    P_ERROR("srrd_create: malloc failed.");
    return -1;
  }

  if (last_up == 0)
    last_up = time(NULL) - 10;

  snprintf(pdp_step_str, sizeof(pdp_step_str), "%lu", pdp_step);
  snprintf(last_up_str, sizeof(last_up_str), "%lu", (unsigned long)last_up);

  new_argv[0] = "create";
  new_argv[1] = (void *)filename;
  new_argv[2] = "-s";
  new_argv[3] = pdp_step_str;
  new_argv[4] = "-b";
  new_argv[5] = last_up_str;

  memcpy(new_argv + 6, argv, argc * sizeof(char *));
  new_argv[new_argc] = NULL;

  pthread_mutex_lock(&librrd_lock);
  optind = 0; /* bug in librrd? */
  rrd_clear_error();

  status = rrd_create(new_argc, new_argv);
  pthread_mutex_unlock(&librrd_lock);

  if (status != 0) {
    P_WARNING("srrd_create: rrd_create (%s) failed: %s", filename,
              rrd_get_error());
  }

  sfree(new_argv);

  return status;
} /* }}} int srrd_create */
コード例 #4
0
ファイル: utils_rrdcreate.c プロジェクト: BrandonArp/collectd
static void *srrd_create_thread(void *targs) /* {{{ */
{
  srrd_create_args_t *args = targs;
  char tmpfile[PATH_MAX];
  int status;

  status = lock_file(args->filename);
  if (status != 0) {
    if (status == EEXIST)
      P_NOTICE("srrd_create_thread: File \"%s\" is already being created.",
               args->filename);
    else
      P_ERROR("srrd_create_thread: Unable to lock file \"%s\".",
              args->filename);
    srrd_create_args_destroy(args);
    return 0;
  }

  snprintf(tmpfile, sizeof(tmpfile), "%s.async", args->filename);

  status = srrd_create(tmpfile, args->pdp_step, args->last_up, args->argc,
                       (void *)args->argv);
  if (status != 0) {
    P_WARNING("srrd_create_thread: srrd_create (%s) returned status %i.",
              args->filename, status);
    unlink(tmpfile);
    unlock_file(args->filename);
    srrd_create_args_destroy(args);
    return 0;
  }

  status = rename(tmpfile, args->filename);
  if (status != 0) {
    P_ERROR("srrd_create_thread: rename (\"%s\", \"%s\") failed: %s", tmpfile,
            args->filename, STRERRNO);
    unlink(tmpfile);
    unlock_file(args->filename);
    srrd_create_args_destroy(args);
    return 0;
  }

  DEBUG("srrd_create_thread: Successfully created RRD file \"%s\".",
        args->filename);

  unlock_file(args->filename);
  srrd_create_args_destroy(args);

  return 0;
} /* }}} void *srrd_create_thread */
コード例 #5
0
ファイル: index.cpp プロジェクト: zilongwhu/agile-se
int Index::dump(const char *path)
{
    P_WARNING("start to dump Index");

    std::string path2;
    if (NULL == path) {
        path2 = m_dual_dir.writeable_path();
    } else {
        path2 = path;
    }
    for (size_t i = 0; i < m_index.size(); ++i)
    {
        if (m_index[i])
        {
            std::string dir = path2 + "/" + m_level2dirname[i];
            if (mk_dir(dir))
            {
                if (this->is_base_mode())
                {
                    m_index[i]->try2merge(true);
                }
                P_WARNING("start to dump at: %s", dir.c_str());
                m_index[i]->dump(dir.c_str());
                P_WARNING("dump ok at: %s", dir.c_str());
            }
        }
    }

    if (m_inc_reader.dumpMeta(path2.c_str(), m_conf.index_meta_file.c_str()) < 0)
    {
        P_WARNING("failed to dump increment reader meta");
        return -1;
    }
    FILE *fp = ::fopen((path2 + "/" + m_conf.index_meta_file.c_str()).c_str(), "a+");
    if (fp)
    {
        ::fprintf(fp, "\n");
        for (size_t i = 0; i < m_index.size(); ++i)
        {
            if (m_index[i])
            {
                ::fprintf(fp, "doc num of %s: %d\n",
                        m_level2dirname[i].c_str(), int(m_index[i]->doc_num()));
            }
        }
        ::fclose(fp);
    }

    if (NULL == path && m_dual_dir.switch_using() < 0)
    {
        P_WARNING("failed to switch using file");
    }

    P_WARNING("dump Index ok");
    return 0;
}
コード例 #6
0
ファイル: monreader.c プロジェクト: 274914765/C
static void mon_iucv_message_pending(struct iucv_path *path,
                     struct iucv_message *msg)
{
    struct mon_private *monpriv = path->private;

    P_DEBUG("IUCV message pending\n");
    memcpy(&monpriv->msg_array[monpriv->write_index]->msg,
           msg, sizeof(*msg));
    if (atomic_inc_return(&monpriv->msglim_count) == MON_MSGLIM) {
        P_WARNING("IUCV message pending, message limit (%i) reached\n",
              MON_MSGLIM);
        monpriv->msg_array[monpriv->write_index]->msglim_reached = 1;
    }
    monpriv->write_index = (monpriv->write_index + 1) % MON_MSGLIM;
    atomic_inc(&monpriv->read_ready);
    wake_up_interruptible(&mon_read_wait_queue);
}
コード例 #7
0
/*!
 * \brief Deletes an Iterator
 * \ingroup MB_API
 * \param[in,out] itr_ptr Address of Iterator Handle
 * 
 * Upon successful removal of the reference to the Iterator from the 
 * ::MBI_OM_iterator ObjectMap, we first delete the pooled-list associated 
 * with the Iterator and then deallocate the Iterator object.
 * 
 * \note It is valid to delete a null Iterator (::MB_NULL_ITERATOR). The routine
 * will return immediately with ::MB_SUCCESS.
 * 
 * Possible return codes:
 *  - ::MB_SUCCESS 
 *  - ::MB_ERR_INVALID (invalid Iterator given) 
 *  - ::MB_ERR_INTERNAL (possible bug. Recompile and run in debug mode for hints)
 */
int MB_Iterator_Delete(MBt_Iterator *itr_ptr) {
	
    int rc;
    MBIt_Iterator *iter;

    /* nothing to do for null iterator */
    if (*itr_ptr == MB_NULL_ITERATOR)
    {
        P_WARNING("Deletion of null iterator (MB_NULL_ITERATOR)");
        return MB_SUCCESS;
    }
    
    /* pop iterator from object map */
    assert(MBI_OM_iterator != NULL);
    assert(MBI_OM_iterator->type == OM_TYPE_ITERATOR);
    iter = (MBIt_Iterator *)MBI_objmap_pop(MBI_OM_iterator, (OM_key_t)*itr_ptr);
    if (iter == NULL) 
    {
        P_FUNCFAIL("Invalid iterator handle (%d)", (int)*itr_ptr);
        return MB_ERR_INVALID;
    }
    
    assert(iter != NULL);
    assert(iter->data != NULL);
    
    /* free memory used by pooled list */
    rc = pl_delete(&(iter->data));
    assert(rc == PL_SUCCESS);
    
    /* deallocate iterator object */
    free(iter);
    
    if (rc != PL_SUCCESS) 
    {
        P_FUNCFAIL("pl_delete() returned with err code %d", rc);
        return MB_ERR_INTERNAL;
    }
    
    P_INFO("Deleted iterator (%d)", (int)*itr_ptr);
    *itr_ptr = MB_NULL_ITERATOR;
    
    return MB_SUCCESS;
}
コード例 #8
0
ファイル: monreader.c プロジェクト: 274914765/C
static struct mon_msg *mon_next_message(struct mon_private *monpriv)
{
    struct mon_msg *monmsg;

    if (!atomic_read(&monpriv->read_ready))
        return NULL;
    monmsg = monpriv->msg_array[monpriv->read_index];
    if (unlikely(monmsg->replied_msglim)) {
        monmsg->replied_msglim = 0;
        monmsg->msglim_reached = 0;
        monmsg->pos = 0;
        monmsg->mca_offset = 0;
        P_WARNING("read, message limit reached\n");
        monpriv->read_index = (monpriv->read_index + 1) %
                      MON_MSGLIM;
        atomic_dec(&monpriv->read_ready);
        return ERR_PTR(-EOVERFLOW);
    }
    return monmsg;
}
コード例 #9
0
ファイル: utils_rrdcreate.c プロジェクト: BrandonArp/collectd
/*
 * Public functions
 */
int cu_rrd_create_file(const char *filename, /* {{{ */
                       const data_set_t *ds, const value_list_t *vl,
                       const rrdcreate_config_t *cfg) {
  char **argv;
  int argc;
  char **rra_def = NULL;
  int rra_num;
  char **ds_def = NULL;
  int ds_num;
  int status = 0;
  time_t last_up;
  unsigned long stepsize;

  if (check_create_dir(filename))
    return -1;

  if ((rra_num = rra_get(&rra_def, vl, cfg)) < 1) {
    P_ERROR("cu_rrd_create_file failed: Could not calculate RRAs");
    return -1;
  }

  if ((ds_num = ds_get(&ds_def, ds, vl, cfg)) < 1) {
    P_ERROR("cu_rrd_create_file failed: Could not calculate DSes");
    rra_free(rra_num, rra_def);
    return -1;
  }

  argc = ds_num + rra_num;

  if ((argv = malloc(sizeof(*argv) * (argc + 1))) == NULL) {
    P_ERROR("cu_rrd_create_file failed: %s", STRERRNO);
    rra_free(rra_num, rra_def);
    ds_free(ds_num, ds_def);
    return -1;
  }

  memcpy(argv, ds_def, ds_num * sizeof(char *));
  memcpy(argv + ds_num, rra_def, rra_num * sizeof(char *));
  argv[ds_num + rra_num] = NULL;

  last_up = CDTIME_T_TO_TIME_T(vl->time);
  if (last_up <= 0)
    last_up = time(NULL);
  last_up -= 1;

  if (cfg->stepsize > 0)
    stepsize = cfg->stepsize;
  else
    stepsize = (unsigned long)CDTIME_T_TO_TIME_T(vl->interval);

  if (cfg->async) {
    status = srrd_create_async(filename, stepsize, last_up, argc,
                               (const char **)argv);
    if (status != 0)
      P_WARNING("cu_rrd_create_file: srrd_create_async (%s) "
                "returned status %i.",
                filename, status);
  } else /* synchronous */
  {
    status = lock_file(filename);
    if (status != 0) {
      if (status == EEXIST)
        P_NOTICE("cu_rrd_create_file: File \"%s\" is already being created.",
                 filename);
      else
        P_ERROR("cu_rrd_create_file: Unable to lock file \"%s\".", filename);
    } else {
      status =
          srrd_create(filename, stepsize, last_up, argc, (const char **)argv);

      if (status != 0) {
        P_WARNING("cu_rrd_create_file: srrd_create (%s) returned status %i.",
                  filename, status);
      } else {
        DEBUG("cu_rrd_create_file: Successfully created RRD file \"%s\".",
              filename);
      }
      unlock_file(filename);
    }
  }

  free(argv);
  ds_free(ds_num, ds_def);
  rra_free(rra_num, rra_def);

  return status;
} /* }}} int cu_rrd_create_file */
コード例 #10
0
ファイル: pdir-posix.c プロジェクト: saprykin/plibsys
P_LIB_API PDirEntry *
p_dir_get_next_entry (PDir	*dir,
		      PError	**error)
{
	PDirEntry	*ret;
#ifdef P_DIR_NEED_BUF_ALLOC
	struct dirent	*dirent_st;
#else
	struct dirent	dirent_st;
#endif
	struct stat	sb;
	pchar		*entry_path;
	psize		path_len;
#ifdef P_DIR_NEED_BUF_ALLOC
	pint		name_max;
#endif

	if (P_UNLIKELY (dir == NULL || dir->dir == NULL)) {
		p_error_set_error_p (error,
				     (pint) P_ERROR_IO_INVALID_ARGUMENT,
				     0,
				     "Invalid input argument");
		return NULL;
	}

#if defined(P_OS_SOLARIS)
	name_max = (pint) (FILENAME_MAX);
#elif defined(P_OS_SCO) || defined(P_OS_IRIX)
	name_max = (pint) pathconf (dir->orig_path, _PC_NAME_MAX);

	if (name_max == -1) {
		if (p_error_get_last_system () == 0)
			name_max = _POSIX_PATH_MAX;
		else {
			p_error_set_error_p (error,
					     (pint) P_ERROR_IO_FAILED,
					     0,
					     "Failed to get NAME_MAX using pathconf()");
			return NULL;
		}
	}
#elif defined(P_OS_QNX6) || defined(P_OS_UNIXWARE) || defined(P_OS_HAIKU)
	name_max = (pint) (NAME_MAX);
#endif

#ifdef P_DIR_NEED_BUF_ALLOC
	if (P_UNLIKELY ((dirent_st = p_malloc0 (sizeof (struct dirent) + name_max + 1)) == NULL)) {
		p_error_set_error_p (error,
				     (pint) P_ERROR_IO_NO_RESOURCES,
				     0,
				     "Failed to allocate memory for internal directory entry");
		return NULL;
	}

#  ifdef P_DIR_NEED_SIMPLE_R
	if ((dir->dir_result = readdir_r (dir->dir, dirent_st)) == NULL) {
		if (P_UNLIKELY (p_error_get_last_system () != 0)) {
			p_error_set_error_p (error,
					     (pint) p_error_get_last_io (),
					     p_error_get_last_system (),
					     "Failed to call readdir_r() to read directory stream");
			p_free (dirent_st);
			return NULL;
		}
	}
#  else
	if (P_UNLIKELY (readdir_r (dir->dir, dirent_st, &dir->dir_result) != 0)) {
		p_error_set_error_p (error,
				     (pint) p_error_get_last_io (),
				     p_error_get_last_system (),
				     "Failed to call readdir_r() to read directory stream");
		p_free (dirent_st);
		return NULL;
	}
#  endif
#else
	if (P_UNLIKELY (readdir_r (dir->dir, &dirent_st, &dir->dir_result) != 0)) {
		p_error_set_error_p (error,
				     (pint) p_error_get_last_io (),
				     p_error_get_last_system (),
				     "Failed to call readdir_r() to read directory stream");
		return NULL;
	}
#endif

	if (dir->dir_result == NULL) {
#ifdef P_DIR_NEED_BUF_ALLOC
		p_free (dirent_st);
#endif
		return NULL;
	}

	if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PDirEntry))) == NULL)) {
		p_error_set_error_p (error,
				     (pint) P_ERROR_IO_NO_RESOURCES,
				     0,
				     "Failed to allocate memory for directory entry");
#ifdef P_DIR_NEED_BUF_ALLOC
		p_free (dirent_st);
#endif
		return NULL;
	}

#ifdef P_DIR_NEED_BUF_ALLOC
	ret->name = p_strdup (dirent_st->d_name);
	p_free (dirent_st);
#else
	ret->name = p_strdup (dirent_st.d_name);
#endif

	path_len = strlen (dir->path);

	if (P_UNLIKELY ((entry_path = p_malloc0 (path_len + strlen (ret->name) + 2)) == NULL)) {
		P_WARNING ("PDir::p_dir_get_next_entry: failed to allocate memory for stat()");
		ret->type = P_DIR_ENTRY_TYPE_OTHER;
		return ret;
	}

	strcat (entry_path, dir->path);
	*(entry_path + path_len) = '/';
	strcat (entry_path + path_len + 1, ret->name);

	if (P_UNLIKELY (stat (entry_path, &sb) != 0)) {
		P_WARNING ("PDir::p_dir_get_next_entry: stat() failed");
		ret->type = P_DIR_ENTRY_TYPE_OTHER;
		p_free (entry_path);
		return ret;
	}

	p_free (entry_path);

	if (S_ISDIR (sb.st_mode))
		ret->type = P_DIR_ENTRY_TYPE_DIR;
	else if (S_ISREG (sb.st_mode))
		ret->type = P_DIR_ENTRY_TYPE_FILE;
	else
		ret->type = P_DIR_ENTRY_TYPE_OTHER;

	return ret;
}
コード例 #11
0
ファイル: index.cpp プロジェクト: zilongwhu/agile-se
int Index::init(const char *path, const char *file)
{
    P_WARNING("start to init Index");

    Config conf(path, file);
    if (conf.parse() < 0)
    {
        P_WARNING("failed to load [%s][%s]", path, file);
        return -1;
    }
    m_conf.index_path = conf["INDEX_PATH"];
    m_conf.index_meta_file = conf["INDEX_META_FILE"];
    m_conf.dump_flag_file = conf["DUMP_FLAG_FILE"];
    m_conf.inc_processor = conf["INC_PROCESSOR"];
    if (!parseInt32(conf["INC_DAS_WARNING_TIME"], m_conf.inc_das_warning_time))
    {
        m_conf.inc_das_warning_time = 60*60*24*3; /* 默认3天没更新则报警 */
    }

    P_WARNING("Index Confs:");
    P_WARNING("    [INDEX_PATH]: %s", m_conf.index_path.c_str());
    P_WARNING("    [INDEX_META_FILE]: %s", m_conf.index_meta_file.c_str());
    P_WARNING("    [DUMP_FLAG_FILE]: %s", m_conf.dump_flag_file.c_str());
    P_WARNING("    [INC_PROCESSOR]: %s", m_conf.inc_processor.c_str());
    P_WARNING("    [INC_DAS_WARNING_TIME]: %d ms", m_conf.inc_das_warning_time);

    thread_func_t proc = NULL;
    {
        std::map<std::string, thread_func_t>::const_iterator it =
            s_inc_processors.find(std::string(m_conf.inc_processor.c_str()));
        if (it == s_inc_processors.end())
        {
            P_WARNING("unregistered inc processor[%s]", m_conf.inc_processor.c_str());
            return -1;
        }
        proc = it->second;
        if (proc == NULL)
        {
            P_WARNING("invalid registered inc processor[%s]", m_conf.inc_processor.c_str());
            return -1;
        }
    }

    if (m_dual_dir.init(m_conf.index_path.c_str()) < 0)
    {
        P_WARNING("failed to init index path");
        return -1;
    }
    m_dump_fw.create(m_conf.dump_flag_file.c_str());

    std::string using_path = m_dual_dir.using_path();

    if (m_inc_reader.init(using_path.c_str(), m_conf.index_meta_file.c_str()) < 0)
    {
        P_WARNING("failed to init increment reader[%s][%s]",
                using_path.c_str(), m_conf.index_meta_file.c_str());
        return -1;
    }

    uint32_t level_num = 0;
    if (parseUInt32(conf["level_num"], level_num))
    {
        for (uint32_t i = 0; i < level_num; ++i)
        {
            char tmpbuf[256];
            ::snprintf(tmpbuf, sizeof tmpbuf, "level_%d", i);
            uint32_t level = 0;
            std::string conf_path;
            std::string conf_file;
            std::string level_name;
            if (!parseUInt32(conf[tmpbuf], level))
            {
                P_WARNING("failed to parse %s", tmpbuf);
                goto FAIL;
            }
            if (level >= m_index.size())
            {
                m_index.resize(level + 1, NULL);
            }
            ::snprintf(tmpbuf, sizeof tmpbuf, "level_%d_conf_path", i);
            conf_path = conf[tmpbuf];
            ::snprintf(tmpbuf, sizeof tmpbuf, "level_%d_conf_file", i);
            conf_file = conf[tmpbuf];
            ::snprintf(tmpbuf, sizeof tmpbuf, "level_%d_name", i);
            level_name = conf[tmpbuf];
            m_index[level] = new (std::nothrow) LevelIndex;
            if (NULL == m_index[level] || m_index[level]->
                    init(conf_path.c_str(), conf_file.c_str()) < 0)
            {
                goto FAIL;
            }
            ::snprintf(tmpbuf, sizeof tmpbuf, "L%u_%s", level, level_name.c_str());
            m_level2dirname[level] = tmpbuf;
        }
        if (0)
        {
FAIL:
            for (size_t i = 0; i < m_index.size(); ++i)
            {
                if (m_index[i])
                {
                    delete m_index[i];
                }
            }
            m_index.clear();
            return -1;
        }

        std::map<size_t, std::string>::const_iterator it = m_level2dirname.begin();
        while (it != m_level2dirname.end())
        {
            P_WARNING("level[%d]'s dirname: %s", int(it->first), it->second.c_str());
            ++it;
        }
    }
    if (m_level2dirname.size() == 0)
    {
        P_WARNING("must have at least one level");
        return -1;
    }
    P_WARNING("level num: %u", level_num);

    int ret = ::pthread_create(&m_inc_tid, NULL, proc, this);
    if (ret != 0)
    {
        P_WARNING("failed to create increment_thread, ret=%d", ret);
        return -1;
    }

    P_WARNING("init Index ok");
    return 0;
}
コード例 #12
0
ファイル: parser.cpp プロジェクト: zilongwhu/agile-se
int infix2postfix(const std::string &query, std::vector<std::string> &result)
{
    char buffer[4096];

    result.clear();

    char ch;
    int len = 0;
    for (size_t i = 0; i < query.length(); ++i)
    {
        ch = query.at(i);
        if (::isspace(ch) == 0)
        {
            if (!is_valid_trigger_char(ch))
            {
                P_WARNING("invalid char[%c] in query[%s]", ch, query.c_str());
                return -1;
            }
            buffer[len++] = ch;
            if (len >= int(sizeof(buffer)))
            {
                P_WARNING("too long query[%s]", query.c_str());
                return -1;
            }
        }
    }
    buffer[len] = '\0';
    P_TRACE("clean query[%s]", buffer);

    int start = -1;
    int last_token = 0;
    std::stack<char> op_stack;
    for (int i = 0; i < len; )
    {
        ch = buffer[i];
        if (ch >= '0' && ch <= '9')
        {
            start = i++;
            while (i < len)
            {
                ch = buffer[i];
                if (ch >= '0' && ch <= '9')
                {
                    ++i;
                }
                else break;
            }
            result.push_back(std::string(buffer + start, i - start));
            P_TRACE("get token[%s]", result[result.size() - 1].c_str());
            last_token = 1;
        }
        else if (ch == '(')
        {
            if (last_token == 1)
            {
                P_WARNING("invalid operator '('");
                return -1;
            }
            op_stack.push(ch);
            P_TRACE("meet operator '('");
            last_token = 0;
            ++i;
        }
        else if (ch == ')')
        {
            if (last_token != 1)
            {
                P_WARNING("invalid operator ')', has nothing in ()");
                return -1;
            }
            P_TRACE("meet operator ')'");
            bool matched = false;
            while (op_stack.size() > 0)
            {
                char op = op_stack.top();
                op_stack.pop();
                if (op == '(')
                {
                    matched = true;
                    break;
                }
                result.push_back(std::string(&op, 1));
                P_TRACE("push back operator[%c]", op);
            }
            if (!matched)
            {
                P_WARNING("unmatched operator ')'");
                return -1;
            }
            last_token = 1;
            ++i;
        }
        else
        {
            if (last_token != 1)
            {
                P_WARNING("operator '%c' must has left value", ch);
                return -1;
            }
            P_TRACE("meet operator[%c]", ch);
            while (op_stack.size() > 0)
            {
                char op = op_stack.top();
                if (op == '(')
                {
                    break;
                }
                if (g_operator_priority[uint8_t(op)] > g_operator_priority[uint8_t(ch)])
                {
                    break;
                }
                op_stack.pop();
                result.push_back(std::string(&op, 1));
                P_TRACE("push back operator[%c]", op);
            }
            op_stack.push(ch);
            last_token = 2;
            ++i;
        }
    }
    while (op_stack.size() > 0)
    {
        char op = op_stack.top();
        if (op == '(')
        {
            P_WARNING("unmatched operator '('");
            return -1;
        }
        op_stack.pop();
        result.push_back(std::string(&op, 1));
        P_TRACE("push back operator[%c]", op);
    }
    P_TRACE("parsed query[%s] ok", query.c_str());
    for (size_t i = 0; i < result.size(); ++i)
    {
        P_TRACE("parsed tokens[%03d]: %s", int(i), result[i].c_str());
    }
    return 0;
}