/***************************************************************************** * * MODULE: DBF driver * * AUTHOR(S): Radim Blazek, Daniel Calvelo * * PURPOSE: Simple driver for reading and writing dbf files * * COPYRIGHT: (C) 2000,2005 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. * * DBF API: http://shapelib.maptools.org/dbf_api.html *****************************************************************************/ #include #include #include #include #include #include #include #include "globals.h" #include "proto.h" /* Results of eval_node */ #define NODE_FALSE 0 #define NODE_TRUE 1 #define NODE_VALUE 2 #define NODE_NULL 3 #define NODE_ERROR 4 int yyparse(void); void get_col_def ( SQLPSTMT *st, int col, int *type, int *width, int *decimals ); int sel(SQLPSTMT * st, int tab, int **set); int eval_val(int tab, int row, int col, SQLPVALUE * inval, SQLPVALUE * result); int set_val(int tab, int row, int col, SQLPVALUE * val); double eval_node(SQLPNODE *, int, int, SQLPVALUE *); int eval_node_type(SQLPNODE *, int); int execute(char *sql, cursor * c) { int i, j, tab, ret; SQLPSTMT *st; ROW *dbrows; VALUE *dbval; int row, nrows; int *cols, ncols, col; int *selset; int dtype, stype; int width, decimals; char *tmpsql, name[500]; SQLPVALUE *calctmp; /* store for calculated values in UPDATE, if any */ /* parse sql statement */ /* I don't know why, but if the statement ends by string in quotes 'xxx' and is not * followed by space or '\n' it is not parsed properly -> */ tmpsql = (char*) G_malloc ( strlen(sql) + 2 ); sprintf ( tmpsql, "%s ", sql ); st = sqpInitStmt(); st->stmt = tmpsql; sqpInitParser(st); if (yyparse() != 0) { sqpFreeStmt(st); G_free ( tmpsql) ; append_error("SQL parser error in statement:\n%s\n", sql); return DB_FAILED; } G_free ( tmpsql) ; G_debug (3, "SQL statement parsed successfully" ); /* sqpPrintStmt(st); */ /* debug output only */ /* find table */ tab = find_table(st->table); if (tab < 0 && st->command != SQLP_CREATE) { append_error("Table '%s' doesn't exist.\n", st->table); return DB_FAILED; } /* For DROP we have to call load_table_head() because it reads permissions */ if ((st->command != SQLP_CREATE)) { ret = load_table_head(tab); if ( ret == DB_FAILED ) { append_error( "Cannot load table head.\n"); return DB_FAILED; } } if ((st->command == SQLP_DROP) || (st->command == SQLP_DELETE) || (st->command == SQLP_INSERT) || (st->command == SQLP_UPDATE) || (st->command == SQLP_ADD_COLUMN) ) { if ( db.tables[tab].write == FALSE ) { append_error( "Cannot modify table, don't have write permission for DBF file.\n"); return DB_FAILED; } } /* find columns */ ncols = st->nCol; if (st->command == SQLP_INSERT || st->command == SQLP_SELECT || st->command == SQLP_UPDATE) { if (ncols > 0) { /* colums were specified */ cols = (int *) G_malloc (ncols * sizeof(int)); for (i = 0; i < ncols; i++) { cols[i] = find_column(tab, st->Col[i].s); if ( cols[i] == -1 ) { append_error( "Column '%s' not found\n", st->Col[i].s); return DB_FAILED; } } } else { /* all columns */ ncols = db.tables[tab].ncols; cols = (int *) G_malloc (ncols * sizeof(int)); for (i = 0; i < ncols; i++) cols[i] = i; } } /* check column types */ if (st->command == SQLP_INSERT || st->command == SQLP_UPDATE) { for (i = 0; i < st->nVal; i++) { col = cols[i]; if ( st->Val[i].type != SQLP_NULL && st->Val[i].type != SQLP_EXPR) { dtype = db.tables[tab].cols[col].type; stype = st->Val[i].type; if ((dtype == DBF_INT && stype != SQLP_I) || (dtype == DBF_DOUBLE && stype == SQLP_S) || (dtype == DBF_CHAR && stype != SQLP_S)) { append_error("Incompatible value type.\n"); return DB_FAILED; } } } } /* do command */ switch (st->command) { case (SQLP_ADD_COLUMN): load_table(tab); get_col_def ( st, 0, &dtype, &width, &decimals ); ret = add_column(tab, dtype, st->Col[0].s, width, decimals); if ( ret == DB_FAILED ) { append_error("Cannot add column.\n"); return DB_FAILED; } /* Add column to each row */ for ( i = 0; i < db.tables[tab].nrows; i++ ) { db.tables[tab].rows[i].values = (VALUE *) G_realloc ( db.tables[tab].rows[i].values, db.tables[tab].ncols * sizeof(VALUE)); dbval = &(db.tables[tab].rows[i].values[db.tables[tab].ncols-1]); dbval->i = 0; dbval->d = 0.0; dbval->c = NULL; dbval->is_null = 1; } db.tables[tab].updated = TRUE; break; case (SQLP_CREATE): if (tab >= 0) { append_error("Table %s already exists\n", st->table); return DB_FAILED; } sprintf ( name, "%s.dbf", st->table ); add_table(st->table, name ); tab = find_table(st->table); db.tables[tab].read = TRUE; db.tables[tab].write = TRUE; for (i = 0; i < ncols; i++) { get_col_def ( st, i, &dtype, &width, &decimals ); ret = add_column(tab, dtype, st->Col[i].s, width, decimals); if ( ret == DB_FAILED ) { append_error("Cannot create table.\n"); db.tables[tab].alive = FALSE; return DB_FAILED; } } db.tables[tab].described = TRUE; db.tables[tab].loaded = TRUE; db.tables[tab].updated = TRUE; break; case (SQLP_DROP): unlink(db.tables[tab].file); db.tables[tab].alive = FALSE; break; case (SQLP_INSERT): load_table(tab); /* add row */ if (db.tables[tab].nrows == db.tables[tab].arows) { db.tables[tab].arows += 1000; db.tables[tab].rows = (ROW *) G_realloc (db.tables[tab].rows, db.tables[tab].arows * sizeof(ROW)); } dbrows = db.tables[tab].rows; row = db.tables[tab].nrows; dbrows[row].values = (VALUE *) G_calloc (db.tables[tab].ncols, sizeof(VALUE)); dbrows[row].alive = TRUE; /* set to null */ for (i = 0; i < db.tables[tab].ncols; i++) { VALUE *dbval; dbval = &(dbrows[row].values[i]); dbval->is_null = 1; } /* set values */ for (i = 0; i < st->nVal; i++) { col = cols[i]; set_val(tab, row, col, &(st->Val[i])); } db.tables[tab].nrows++; db.tables[tab].updated = TRUE; break; case (SQLP_SELECT): G_debug ( 2, "SELECT"); c->st = st; c->table = tab; c->cols = cols; c->ncols = ncols; c->nrows = sel(st, tab, &(c->set)); if (c->nrows < 0) { append_error("Error in selecting rows\n"); return DB_FAILED; } c->cur = -1; break; case (SQLP_UPDATE): nrows = sel(st, tab, &selset); if (nrows < 0) { append_error("Error in selecting rows\n"); return DB_FAILED; } dbrows = db.tables[tab].rows; /* update rows */ for (i = 0; i < nrows; i++) { SQLPVALUE *temp_p; calctmp = (SQLPVALUE*)G_malloc((st->nVal) * sizeof(SQLPVALUE)); row = selset[i]; for (j = 0; j < st->nVal; j++) { col = cols[j]; eval_val(tab, row, col, &(st->Val[j]), &(calctmp[j])); } temp_p = st->Val; st->Val = calctmp; for (j = 0; j < st->nVal; j++) { col = cols[j]; set_val(tab, row, col, &(st->Val[j])); db.tables[tab].updated = TRUE; } st->Val = temp_p; G_free(calctmp); } break; case (SQLP_DELETE): nrows = sel(st, tab, &selset); if (nrows < 0) { append_error("Error in selecting rows\n"); return DB_FAILED; } dbrows = db.tables[tab].rows; /* delete rows */ for (i = 0; i < nrows; i++) { row = selset[i]; dbrows[row].alive = FALSE; db.tables[tab].updated = TRUE; } break; } if ( st->command != SQLP_SELECT ) { /* because statement is released with cursor */ sqpFreeStmt(st); } return DB_OK; } /* for given parser result and column index finds dbf column definition */ void get_col_def ( SQLPSTMT *st, int col, int *type, int *width, int *decimals ) { switch (st->ColType[col]) { case (SQLP_INTEGER): *type = DBF_INT; *width = 11; *decimals = 0; break; case (SQLP_VARCHAR): *type = DBF_CHAR; *width = st->ColWidth[col]; *decimals = 0; break; case (SQLP_DATE): /* DATE treated as string unless SHAPELIB/DBFLIB supports date type */ *type = DBF_CHAR; *width = 10; /* 2004-01-23 = 10 chars */ *decimals = 0; break; case (SQLP_DOUBLE): *type = DBF_DOUBLE; *width = 20; *decimals = 6; break; } } int eval_val(int tab, int row, int col, SQLPVALUE * inval, SQLPVALUE *val) { double retval; /* XXX */ if ( inval->type == SQLP_EXPR ) { retval = eval_node( inval->expr, tab, row, val ); if ( retval == NODE_NULL ) { val->type = SQLP_NULL; } else if ( retval == NODE_TRUE ) { val->i = 1; val->d = 1.0; val->s = "TRUE"; } else if ( retval == NODE_FALSE ) { val->i = 0; val->d = 0.0; val->s = NULL; } else if ( retval == NODE_VALUE ) { /* Ok, got a value, propagate it to the proper type */ if( val->type == SQLP_I ){ val->d = (double)val->i; val->s = (char*)G_malloc (32*sizeof(char)); sprintf( val->s, "%d", val->i ); }else if( val->type == SQLP_D ){ val->i = (int)val->d; val->s = (char*)G_malloc (32*sizeof(char)); sprintf( val->s, "%g", val->d ); }else if( val->type == SQLP_S ){ val->i = atoi( val->s ); val->d = atof( val->s ); }else{ G_fatal_error("This should not happen: wrong return type in parsing."); } } else if ( retval == NODE_ERROR ) { G_fatal_error("This should not happen: got a wrong expression structure after parsing."); } else { G_fatal_error("Unknown return value calling eval_node from eval_val"); } }else{ /* * TODO: maybe use this function to perform type "conversion", * i.e. setting all of s,i,d to the same "value",as is done with * the result of eval_node above. */ val = inval; } } int set_val(int tab, int row, int col, SQLPVALUE * val) { VALUE *dbval; dbval = &(db.tables[tab].rows[row].values[col]); /* For debugging purposes; see FIXME below fprintf(stderr, "In set_val : "); fprintf(stderr, val->type==SQLP_EXPR?"sqlp_expr": val->type==SQLP_NULL?"sqlp_null": val->type==SQLP_I?"sqlp_i": val->type==SQLP_D?"sqlp_d": val->type==SQLP_S?"sqlp_s": "other"); //DCA fprintf(stderr,"%d\n",val->type); fflush(stderr); */ if ( val->type == SQLP_EXPR ){ eval_val( tab, row, col, val, val ); } /* FIXME: SQLP_NULL is not always properly detected. * This workaround works, since type should be some of these * after passing through eval_val; otherwise it is NULL */ if ( ! ( val->type == SQLP_I || val->type == SQLP_D || val->type == SQLP_S ) ) { dbval->is_null = 1; dbval->c = NULL; dbval->i = 0; dbval->d = 0.0; } else { dbval->is_null = 0; switch (db.tables[tab].cols[col].type) { case DBF_INT: dbval->i = val->i; break; case DBF_CHAR: save_string(dbval, val->s); break; case DBF_DOUBLE: if (val->type == SQLP_I) dbval->d = val->i; else if (val->type == SQLP_D) dbval->d = val->d; break; } } return (1); } /* Comparison of 2 rows */ static int cur_cmp_table; static int cur_cmp_ocol; static int cmp_row ( const void *pa, const void *pb ) { int *row1 = (int*) pa; int *row2 = (int*) pb; char *c1, *c2; int i1, i2; double d1, d2; TABLE *tbl; tbl = &(db.tables[cur_cmp_table]); switch ( tbl->cols[cur_cmp_ocol].type ) { case DBF_CHAR: c1 = tbl->rows[*row1].values[cur_cmp_ocol].c; c2 = tbl->rows[*row2].values[cur_cmp_ocol].c; return ( strcmp(c1, c2) ); break; case DBF_INT: i1 = tbl->rows[*row1].values[cur_cmp_ocol].i; i2 = tbl->rows[*row2].values[cur_cmp_ocol].i; if ( i1 < i2 ) return -1; if ( i1 > i2 ) return 1; return 0; break; case DBF_DOUBLE: d1 = tbl->rows[*row1].values[cur_cmp_ocol].d; d2 = tbl->rows[*row2].values[cur_cmp_ocol].d; if ( d1 < d2 ) return -1; if ( d1 > d2 ) return 1; return 0; break; } return 0; } /* Select records, sets 'selset' to new array of items and returns * number of items or -1 for error */ int sel(SQLPSTMT * st, int tab, int **selset) { int i, ret, condition; int *set; /* pointer to array of indexes to rows */ int aset, nset; G_debug ( 2, "sel(): tab = %d", tab); *selset = NULL; nset = 0; ret = load_table(tab); if ( ret == DB_FAILED ) { append_error( "Cannot load table.\n"); return -1; } aset = 1; set = (int *) G_malloc (aset * sizeof(int)); if (st->upperNodeptr) { int node_type; /* First eval node type */ node_type = eval_node_type( st->upperNodeptr, tab); G_debug(4, "node result type = %d", node_type); if ( node_type == -1 ) { append_error( "Incompatible types in WHERE condition.\n"); return -1; } else if ( node_type == SQLP_S || node_type == SQLP_I || node_type == SQLP_D ) { append_error( "Result of WHERE condition is not of type BOOL.\n"); return -1; } else if ( node_type == SQLP_NULL ) { /* Conditions has undefined result -> nothing selected */ return 0; } else if ( node_type == SQLP_BOOL ) { for (i = 0; i < db.tables[tab].nrows; i++) { SQLPVALUE value; G_debug(4, "row %d", i); condition = eval_node( st->upperNodeptr, tab, i, &value); G_debug(4, "condition = %d", condition); if ( condition == NODE_ERROR ) { /* e.g. division by 0 */ append_error( "Error in evaluation of WHERE condition.\n"); return (-1); } else if ( condition == NODE_TRUE ) { /* true */ if (nset == aset) { aset += 1000; set = (int *) G_realloc (set, aset * sizeof(int)); } set[nset] = i; nset++; } else if ( condition != NODE_FALSE && condition != NODE_NULL ) { /* Should not happen */ append_error( "Unknown result (%d) of WHERE evaluation.\n", condition); return -1; } } } else { /* Should not happen */ append_error( "Unknown WHERE condition type (bug in DBF driver).\n"); return -1; } } else { /* Select all */ aset = db.tables[tab].nrows; set = (int *) G_realloc (set, aset * sizeof(int)); for (i = 0; i < db.tables[tab].nrows; i++) { set[i] = i; } nset = db.tables[tab].nrows; } /* Order */ if ( st->command == SQLP_SELECT && st->orderCol ) { G_debug(3, "Order selection by %s", st->orderCol); /* Find order col */ cur_cmp_ocol = -1; for ( i = 0; i < db.tables[tab].ncols; i++ ) { if ( strcmp ( db.tables[tab].cols[i].name, st->orderCol ) == 0 ) { cur_cmp_ocol = i; break; } } if ( cur_cmp_ocol < 0 ) { append_error( "Cannot find order column '%s'\n", st->orderCol); return -1; } cur_cmp_table = tab; qsort(set, nset, sizeof(int), cmp_row); } *selset = set; return nset; } /* Evaluate node recursively. * * Returns: * NODE_NULL result/value is unknown * NODE_TRUE * NODE_FALSE * NODE_VALUE result is a value stored in 'value' * (if value is not NULL otherwise NODE_NULL is returned and value is not set) * NODE_ERROR e.g. division by 0 * * If results is NODE_VALUE, the 'value' is set, if value is type SQLP_S the string is not duplicated * and only pointer is set -> do not free value->s */ double eval_node(SQLPNODE *nptr, int tab, int row, SQLPVALUE *value) { int left, right; SQLPVALUE left_value, right_value; int ccol; COLUMN *col; VALUE *val; double left_dval, right_dval, dval; /* Note: node types were previously checked by eval_node_type */ G_debug ( 4, "eval_node node_type = %d", nptr->node_type ); switch ( nptr->node_type) { case SQLP_NODE_VALUE: if ( nptr->value.type == SQLP_NULL ) return NODE_NULL; value->type = nptr->value.type; value->s = nptr->value.s; value->i = nptr->value.i; value->d = nptr->value.d; return NODE_VALUE; break; case SQLP_NODE_COLUMN: ccol = find_column(tab, nptr->column_name); col = &(db.tables[tab].cols[ccol]); val = &(db.tables[tab].rows[row].values[ccol]); if ( val->is_null ) return NODE_NULL; switch (col->type) { case DBF_CHAR: value->s = val->c; value->type = SQLP_S; break; case DBF_INT: value->i = val->i; value->type = SQLP_I; break; case DBF_DOUBLE: value->d = val->d; value->type = SQLP_D; break; } return NODE_VALUE; break; case SQLP_NODE_EXPRESSION: /* Note: Some expressions (e.g. NOT) have only one side */ if ( nptr->left ) { left = eval_node ( nptr->left, tab, row, &left_value); G_debug ( 4, " left = %d", left ); if ( left == NODE_ERROR ) return NODE_ERROR; if ( left != NODE_NULL ) { if ( left_value.type == SQLP_I ) left_dval = left_value.i; else left_dval = left_value.d; G_debug ( 4, " left_dval = %f", left_dval ); } } if ( nptr->right ) { right = eval_node ( nptr->right, tab, row, &right_value); G_debug ( 4, " right = %d", right ); if ( right == NODE_ERROR ) return NODE_ERROR; if ( right != NODE_NULL ) { if ( right_value.type == SQLP_I ) right_dval = right_value.i; else right_dval = right_value.d; G_debug ( 4, " right_dval = %f", right_dval ); } } G_debug ( 4, " operator = %d", nptr->oper ); switch ( nptr->oper ) { /* Arithmetical */ case SQLP_ADD: case SQLP_SUBTR: case SQLP_MLTP: case SQLP_DIV: if ( left == NODE_NULL || right == NODE_NULL ) return NODE_NULL; switch ( nptr->oper ) { case SQLP_ADD: dval = left_dval + right_dval; break; case SQLP_SUBTR: dval = left_dval - right_dval; break; case SQLP_MLTP: dval = left_dval * right_dval; break; case SQLP_DIV: if ( right_dval != 0.0) { dval = left_dval / right_dval; } else { append_error ("Division by zero\n"); return NODE_ERROR; } break; } if ( left_value.type == SQLP_I && right_value.type == SQLP_I && ( nptr->oper == SQLP_ADD || nptr->oper == SQLP_SUBTR || nptr->oper == SQLP_MLTP ) ) { value->type = SQLP_I; value->i = (int) dval; } else { value->type = SQLP_D; value->d = dval; } return NODE_VALUE; break; /* Comparison */ /* Operators valid for all type */ case SQLP_EQ: if ( left == NODE_NULL || right == NODE_NULL ) { return NODE_NULL; } else if ( left_value.type == SQLP_S ) { /* we checked before if right is also string */ if ( left_value.s && right_value.s && strcmp(left_value.s,right_value.s) == 0) return NODE_TRUE; else return NODE_FALSE; } else { /* numbers */ if ( left_dval == right_dval ) return NODE_TRUE; else return NODE_FALSE; } break; case SQLP_NE: if ( left == NODE_NULL || right == NODE_NULL ) { return NODE_NULL; } else if ( left_value.type == SQLP_S ) { /* we checked before if right is also string */ if ( left_value.s && right_value.s && strcmp(left_value.s,right_value.s) != 0) return NODE_TRUE; else return NODE_FALSE; } else { /* numbers */ if ( left_dval != right_dval ) return NODE_TRUE; else return NODE_FALSE; } /* Operators valid for numbers */ case SQLP_LT: if ( left == NODE_NULL || right == NODE_NULL ) { return NODE_NULL; } else { if ( left_dval < right_dval ) return NODE_TRUE; else return NODE_FALSE; } case SQLP_LE: if ( left == NODE_NULL || right == NODE_NULL ) { return NODE_NULL; } else { if ( left_dval <= right_dval ) return NODE_TRUE; else return NODE_FALSE; } case SQLP_GT: if ( left == NODE_NULL || right == NODE_NULL ) { return NODE_NULL; } else { if ( left_dval > right_dval ) return NODE_TRUE; else return NODE_FALSE; } case SQLP_GE: if ( left == NODE_NULL || right == NODE_NULL ) { return NODE_NULL; } else { if ( left_dval >= right_dval ) return NODE_TRUE; else return NODE_FALSE; } /* Operator valid for string */ case SQLP_MTCH: if ( left == NODE_NULL || right == NODE_NULL ) { return NODE_NULL; } else { if ( left_value.s && right_value.s && strstr(left_value.s,right_value.s) != NULL) return NODE_TRUE; else return NODE_FALSE; } case SQLP_ISNULL: return right == NODE_NULL ? NODE_TRUE : NODE_FALSE; case SQLP_NOTNULL: return right != NODE_NULL ? NODE_TRUE : NODE_FALSE; /* Logical */ case SQLP_AND: if ( left == NODE_NULL || right == NODE_NULL ) { return NODE_NULL; } else if ( left == NODE_TRUE && right == NODE_TRUE ) { return NODE_TRUE; } else if ( left == NODE_VALUE || right == NODE_VALUE ) { /* Should not happen */ append_error ("Value operand for AND\n"); return NODE_ERROR; } else { return NODE_FALSE; } case SQLP_OR: if ( left == NODE_NULL && right == NODE_NULL ) { return NODE_NULL; } else if ( left == NODE_TRUE || right == NODE_TRUE ) { return NODE_TRUE; } else if ( left == NODE_VALUE || right == NODE_VALUE ) { /* Should not happen */ append_error ("Value operand for OR\n"); return NODE_ERROR; } else { return NODE_FALSE; } case SQLP_NOT: /* sub node stored on the right side */ if ( right == NODE_NULL ) { return NODE_NULL; } else if ( right == NODE_TRUE ) { return NODE_FALSE; } else if ( right == NODE_VALUE ) { /* Should not happen */ append_error ("Value operand for NOT\n"); return NODE_ERROR; } else { return NODE_TRUE; } default: append_error ("Unknown operator %d\n", nptr->oper); return NODE_FALSE; } } return NODE_ERROR; /* Not reached */ } /* Recursively get value/expression type. * Returns: node type (SQLP_S, SQLP_I, SQLP_D, SQLP_NULL, SQLP_BOOL) * -1 on error (if types in expression are not compatible) * * Rules: * Values (in SQL Statement): * SQLP_S -> SQLP_S * SQLP_I -> SQLP_I * SQLP_D -> SQLP_D * SQLP_NULL -> SQLP_NULL * Columns (in dbf table): * DBF_CHAR -> SQLP_S * DBF_INT -> SQLP_I * DBF_DOUBLE -> SQLP_D * Arithetical Expressions : * side1 side2 exp * SQLP_S ALL ALL -> error * SQLP_NULL SQLP_I ALL -> SQLP_NULL * SQLP_NULL SQLP_D ALL -> SQLP_NULL * SQLP_I SQLP_I +,-,* -> SQLP_I * SQLP_I SQLP_I / -> SQLP_D * SQLP_I SQLP_D ALL -> SQLP_D * SQLP_D SQLP_D ALL -> SQLP_D * Comparisons : * side1 side2 exp * SQLP_S SQLP_S =,<>,~ -> SQLP_BOOL * SQLP_S SQLP_S <,<=,>,>= -> error * SQLP_S SQLP_I ALL -> error * SQLP_S SQLP_D ALL -> error * SQLP_I SQLP_I =,<>,<,<=,>,>= -> SQLP_BOOL * SQLP_D SQLP_D =,<>,<,<=,>,>= -> SQLP_BOOL * SQLP_I SQLP_D =,<>,<,<=,>,>= -> SQLP_BOOL * SQLP_I ALL ~ -> error * SQLP_D ALL ~ -> error * SQLP_NULL ALL ALL -> SQLP_NULL * Logical expressions * In general, if we know that the result is NULL regardless actual values it returns SQLP_NULL * so that tests for individual rows are not performed, otherwise SQLP_BOOL * SQLP_BOOL SQLP_BOOL AND -> SQLP_BOOL * SQLP_BOOL SQLP_NULL AND -> SQLP_NULL * SQLP_NULL SQLP_NULL AND -> SQLP_NULL * SQLP_BOOL SQLP_BOOL OR -> SQLP_BOOL * SQLP_BOOL SQLP_NULL OR -> SQLP_BOOL * SQLP_NULL SQLP_NULL OR -> SQLP_NULL * SQLP_BOOL - NOT -> SQLP_BOOL * SQLP_NULL - NOT -> SQLP_NULL */ int eval_node_type(SQLPNODE *nptr, int tab ) { int left, right; int ccol; COLUMN *col = NULL; switch ( nptr->node_type) { case SQLP_NODE_VALUE: return nptr->value.type; break; case SQLP_NODE_COLUMN: ccol = find_column ( tab, nptr->column_name ); if ( ccol == -1 ) { append_error ("Column '%s' not found\n", nptr->column_name); return (-1); } col = &(db.tables[tab].cols[ccol]); switch (col->type) { case DBF_CHAR: return (SQLP_S); break; case DBF_INT: return (SQLP_I); break; case DBF_DOUBLE: return (SQLP_D); break; } break; case SQLP_NODE_EXPRESSION: /* Note: Some expressions (e.g. NOT) have only one side */ if ( nptr->left ) { left = eval_node_type( nptr->left, tab); if ( left == -1 ) return -1; } if ( nptr->right ) { right = eval_node_type( nptr->right, tab); if ( right == -1 ) return -1; } switch ( nptr->oper ) { /* Arithmetical */ case SQLP_ADD: case SQLP_SUBTR: case SQLP_MLTP: case SQLP_DIV: if ( left == SQLP_S || right == SQLP_S ) { append_error ("Arithmetical operation with strings is not allowed\n" ); return -1; } else if ( left == SQLP_NULL || right == SQLP_NULL ) { return SQLP_NULL; } else if ( left == SQLP_I && right == SQLP_I && ( nptr->oper == SQLP_ADD || nptr->oper == SQLP_SUBTR || nptr->oper == SQLP_MLTP ) ) { return SQLP_I; } else { return SQLP_D; } break; /* Comparison */ /* Operators valid for all type */ case SQLP_EQ: case SQLP_NE: if ( ( left == SQLP_S && ( right == SQLP_I || right == SQLP_D ) ) || ( right == SQLP_S && ( left == SQLP_I || left == SQLP_D ) ) ) { append_error ("Comparison between string and number is not allowed\n" ); return -1; } else if ( left == SQLP_NULL || right == SQLP_NULL ) { return SQLP_NULL; } else { return SQLP_BOOL; } /* Operators valid for numbers */ case SQLP_LT: case SQLP_LE: case SQLP_GT: case SQLP_GE: if ( left == SQLP_S || right == SQLP_S ) { append_error ("Comparison '%s' between strings not allowed\n", sqpOperatorName(nptr->oper) ); return -1; } else if ( left == SQLP_NULL || right == SQLP_NULL ) { return SQLP_NULL; } else { return SQLP_BOOL; } /* Operator valid for string */ case SQLP_MTCH: if ( left == SQLP_I || left == SQLP_D || right == SQLP_I || right == SQLP_D ) { append_error ("Match (~) between numbers not allowed\n" ); return -1; } else if ( left == SQLP_NULL || right == SQLP_NULL ) { return SQLP_NULL; } else { return SQLP_BOOL; } case SQLP_ISNULL: case SQLP_NOTNULL: return SQLP_BOOL; /* Logical */ case SQLP_AND: if ( left == SQLP_NULL || right == SQLP_NULL ) { return SQLP_NULL; } else { return SQLP_BOOL; } case SQLP_OR: if ( left == SQLP_NULL && right == SQLP_NULL ) { return SQLP_NULL; } else { return SQLP_BOOL; } case SQLP_NOT: /* sub node stored on the right side */ if ( right == SQLP_NULL ) { return SQLP_NULL; } else { return SQLP_BOOL; } default: append_error ("Unknown operator %d\n", nptr->oper); return -1; } } return -1; /* Not reached */ }