/*! * \file db/dbmi_client/select.c * * \brief DBMI Library (client) - select records from table * * (C) 1999-2008 by the GRASS Development Team * * This program is free software under the GNU General Public * License (>=v2). Read the file COPYING that comes with GRASS * for details. * * \author Joel Jones (CERL/UIUC), Radim Blazek */ #include #include #include #include #include static int cmp(const void *pa, const void *pb) { int *p1 = (int *)pa; int *p2 = (int *)pb; if (*p1 < *p2) return -1; if (*p1 > *p2) return 1; return 0; } static int cmpcat(const void *pa, const void *pb) { dbCatVal *p1 = (dbCatVal *) pa; dbCatVal *p2 = (dbCatVal *) pb; if (p1->cat < p2->cat) return -1; if (p1->cat > p2->cat) return 1; return 0; } static int cmpcatkey(const void *pa, const void *pb) { int *p1 = (int *)pa; dbCatVal *p2 = (dbCatVal *) pb; if (*p1 < p2->cat) return -1; if (*p1 > p2->cat) return 1; return 0; } static int cmpvalueint(const void *pa, const void *pb) { dbCatVal *p1 = (dbCatVal *) pa; dbCatVal *p2 = (dbCatVal *) pb; if (p1->val.i < p2->val.i) return -1; if (p1->val.i > p2->val.i) return 1; return 0; } static int cmpvaluedouble(const void *pa, const void *pb) { dbCatVal *p1 = (dbCatVal *) pa; dbCatVal *p2 = (dbCatVal *) pb; if (p1->val.d < p2->val.d) return -1; if (p1->val.d > p2->val.d) return 1; return 0; } static int cmpvaluestring(const void *pa, const void *pb) { dbCatVal *const *a = pa; dbCatVal *const *b = pb; return strcmp((const char *)a, (const char *)b); } /*! \brief Select array of ordered integers from table/column \param driver DB driver \param tab table name \param col column name \param where where statement \param[out] pval array of ordered integer values \return number of selected values \return -1 on error */ int db_select_int(dbDriver * driver, const char *tab, const char *col, const char *where, int **pval) { int type, more, alloc, count; int *val; char *buf = NULL; const char *sval; dbString stmt; dbCursor cursor; dbColumn *column; dbValue *value; dbTable *table; G_debug(3, "db_select_int()"); if (col == NULL || strlen(col) == 0) { G_warning(_("Missing column name")); return -1; } /* allocate */ alloc = 1000; val = (int *)G_malloc(alloc * sizeof(int)); if (where == NULL || strlen(where) == 0) G_asprintf(&buf, "SELECT %s FROM %s", col, tab); else G_asprintf(&buf, "SELECT %s FROM %s WHERE %s", col, tab, where); G_debug(3, " SQL: %s", buf); db_init_string(&stmt); db_set_string(&stmt, buf); G_free(buf); if (db_open_select_cursor(driver, &stmt, &cursor, DB_SEQUENTIAL) != DB_OK) return (-1); table = db_get_cursor_table(&cursor); column = db_get_table_column(table, 0); /* first column */ if (column == NULL) { return -1; } value = db_get_column_value(column); type = db_get_column_sqltype(column); type = db_sqltype_to_Ctype(type); /* fetch the data */ count = 0; while (1) { if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) return (-1); if (!more) break; if (count == alloc) { alloc += 1000; val = (int *)G_realloc(val, alloc * sizeof(int)); } switch (type) { case (DB_C_TYPE_INT): val[count] = db_get_value_int(value); break; case (DB_C_TYPE_STRING): sval = db_get_value_string(value); val[count] = atoi(sval); break; case (DB_C_TYPE_DOUBLE): val[count] = (int)db_get_value_double(value); break; default: return (-1); } count++; } db_close_cursor(&cursor); db_free_string(&stmt); qsort((void *)val, count, sizeof(int), cmp); *pval = val; return (count); } /*! \brief Select one (first) value from table/column for key/id \param driver DB driver \param tab table name \param key key column name \param id identifier in key column \param col name of column to select the value from \param[out] val dbValue to store within \return number of selected values \return -1 on error */ int db_select_value(dbDriver * driver, const char *tab, const char *key, int id, const char *col, dbValue * val) { int more, count; char *buf = NULL; dbString stmt; dbCursor cursor; dbColumn *column; dbValue *value; dbTable *table; if (key == NULL || strlen(key) == 0) { G_warning(_("Missing key column name")); return -1; } if (col == NULL || strlen(col) == 0) { G_warning(_("Missing column name")); return -1; } G_zero(val, sizeof(dbValue)); G_asprintf(&buf, "SELECT %s FROM %s WHERE %s = %d", col, tab, key, id); db_init_string(&stmt); db_set_string(&stmt, buf); G_free(buf); if (db_open_select_cursor(driver, &stmt, &cursor, DB_SEQUENTIAL) != DB_OK) return (-1); table = db_get_cursor_table(&cursor); column = db_get_table_column(table, 0); /* first column */ value = db_get_column_value(column); /* fetch the data */ count = 0; while (1) { if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) return (-1); if (!more) break; if (count == 0) db_copy_value(val, value); count++; } db_close_cursor(&cursor); db_free_string(&stmt); return (count); } /*! \brief Select pairs key/value to array, values are sorted by key (must be integer) \param driver DB driver \param tab table name \param key key column name \param col value column name \param[out] cvarr dbCatValArray to store within \return number of selected values \return -1 on error */ int db_select_CatValArray(dbDriver * driver, const char *tab, const char *key, const char *col, const char *where, dbCatValArray * cvarr) { int i, type, more, nrows, ncols; char *buf = NULL; dbString stmt; dbCursor cursor; dbColumn *column; dbValue *value; dbTable *table; G_debug(3, "db_select_CatValArray ()"); if (key == NULL || strlen(key) == 0) { G_warning(_("Missing key column name")); return -1; } if (col == NULL || strlen(col) == 0) { G_warning(_("Missing column name")); return -1; } db_init_string(&stmt); if (strcmp(key, col) == 0) { ncols = 1; G_asprintf(&buf, "SELECT %s FROM %s", key, tab); } else { ncols = 2; G_asprintf(&buf, "SELECT %s, %s FROM %s", key, col, tab); } db_set_string(&stmt, buf); G_free(buf); if (where != NULL && strlen(where) > 0) { db_append_string(&stmt, " WHERE "); db_append_string(&stmt, where); } G_debug(3, " SQL: %s", db_get_string(&stmt)); if (db_open_select_cursor(driver, &stmt, &cursor, DB_SEQUENTIAL) != DB_OK) return (-1); nrows = db_get_num_rows(&cursor); G_debug(3, " %d rows selected", nrows); if (nrows < 0) { G_warning(_("Unable select records from table <%s>"), tab); db_close_cursor(&cursor); db_free_string(&stmt); return -1; } db_CatValArray_alloc(cvarr, nrows); table = db_get_cursor_table(&cursor); /* Check if key column is integer */ column = db_get_table_column(table, 0); type = db_sqltype_to_Ctype(db_get_column_sqltype(column)); G_debug(3, " key type = %d", type); if (type != DB_C_TYPE_INT) { G_warning(_("Key column type is not integer")); db_close_cursor(&cursor); db_free_string(&stmt); return -1; } if (ncols == 2) { column = db_get_table_column(table, 1); type = db_sqltype_to_Ctype(db_get_column_sqltype(column)); G_debug(3, " col type = %d", type); /* if ( type != DB_C_TYPE_INT && type != DB_C_TYPE_DOUBLE ) { G_fatal_error ( "Column type not supported by db_select_to_array()" ); } */ } cvarr->ctype = type; /* fetch the data */ for (i = 0; i < nrows; i++) { if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) return (-1); column = db_get_table_column(table, 0); /* first column */ value = db_get_column_value(column); cvarr->value[i].cat = db_get_value_int(value); if (ncols == 2) { column = db_get_table_column(table, 1); value = db_get_column_value(column); } cvarr->value[i].isNull = value->isNull; switch (type) { case (DB_C_TYPE_INT): if (value->isNull) cvarr->value[i].val.i = 0; else cvarr->value[i].val.i = db_get_value_int(value); break; case (DB_C_TYPE_DOUBLE): if (value->isNull) cvarr->value[i].val.d = 0.0; else cvarr->value[i].val.d = db_get_value_double(value); break; case (DB_C_TYPE_STRING): cvarr->value[i].val.s = (dbString *) malloc(sizeof(dbString)); db_init_string(cvarr->value[i].val.s); if (!(value->isNull)) db_set_string(cvarr->value[i].val.s, db_get_value_string(value)); break; case (DB_C_TYPE_DATETIME): cvarr->value[i].val.t = (dbDateTime *) calloc(1, sizeof(dbDateTime)); if (!(value->isNull)) memcpy(cvarr->value[i].val.t, &(value->t), sizeof(dbDateTime)); break; default: return (-1); } } cvarr->n_values = nrows; db_close_cursor(&cursor); db_free_string(&stmt); db_CatValArray_sort(cvarr); return nrows; } /*! \brief Sort key/value array by key \param[in,out] arr dbCatValArray (key/value array) */ void db_CatValArray_sort(dbCatValArray * arr) { qsort((void *)arr->value, arr->n_values, sizeof(dbCatVal), cmpcat); } /*! \brief Sort key/value array by value \param[in,out] arr dbCatValArray (key/value array) \return DB_OK on success \return DB_FAILED on error */ int db_CatValArray_sort_by_value(dbCatValArray * arr) { switch (arr->ctype) { case (DB_C_TYPE_INT): qsort((void *)arr->value, arr->n_values, sizeof(dbCatVal), cmpvalueint); break; case (DB_C_TYPE_DOUBLE): qsort((void *)arr->value, arr->n_values, sizeof(dbCatVal), cmpvaluedouble); break; case (DB_C_TYPE_STRING): qsort((void *)arr->value, arr->n_values, sizeof(dbCatVal), cmpvaluestring); break; case (DB_C_TYPE_DATETIME): /* is cmpvaluestring right here ? */ qsort((void *)arr->value, arr->n_values, sizeof(dbCatVal), cmpvaluestring); break; default: return (DB_FAILED); } return (DB_OK); } /*! \brief Find value by key \param arr dbCatValArray (key/value array) \param key key value \param[out] cv dbCatVal structure (key/value) to store within \return DB_OK on success \return DB_FAILED on error */ int db_CatValArray_get_value(dbCatValArray * arr, int key, dbCatVal ** cv) { dbCatVal *catval; catval = bsearch((void *)&key, arr->value, arr->n_values, sizeof(dbCatVal), cmpcat); if (catval == NULL) { return DB_FAILED; } *cv = catval; return DB_OK; } /*! \brief Find value (integer) by key \param arr dbCatValArray (key/value array) \param key key value \param[out] val found value (integer) \return DB_OK on success \return DB_FAILED on error */ int db_CatValArray_get_value_int(dbCatValArray * arr, int key, int *val) { dbCatVal *catval; catval = bsearch((void *)&key, arr->value, arr->n_values, sizeof(dbCatVal), cmpcat); if (catval == NULL) { return DB_FAILED; } *val = catval->val.i; return DB_OK; } /*! \brief Find value (double) by key \param arr dbCatValArray (key/value array) \param key key value \param[out] val found value (double) \return DB_OK on success \return DB_FAILED on error */ int db_CatValArray_get_value_double(dbCatValArray * arr, int key, double *val) { dbCatVal *catval; G_debug(3, "db_CatValArray_get_value_double(), key = %d", key); catval = bsearch((void *)&key, arr->value, arr->n_values, sizeof(dbCatVal), cmpcatkey); if (catval == NULL) { return DB_FAILED; } *val = catval->val.d; return DB_OK; }