//-< QUERY.H >-------------------------------------------------------*--------*
// GigaBASE                  Version 1.0         (c) 1999  GARRET    *     ?  *
// (Post Relational Database Management System)                      *   /\|  *
//                                                                   *  /  \  *
//                          Created:     20-Nov-98    K.A. Knizhnik  * / [] \ *
//                          Last update: 10-Dec-98    K.A. Knizhnik  * GARRET *
//-------------------------------------------------------------------*--------*
// Constructing and hashing database query statements
//-------------------------------------------------------------------*--------*

#ifndef __QUERY_H__
#define __QUERY_H__

BEGIN_GIGABASE_NAMESPACE

class GIGABASE_DLL_ENTRY dbQueryElement {
    friend class dbQuery;
    friend class dbCompiler;
    friend class dbQueryExpression;
    friend class dbQueryElementAllocator;
  public:
    enum ElementType {
	qExpression, // part of SQL expression
	qVarBool,
	qVarInt1,
	qVarInt2,
	qVarInt4,
	qVarInt8,
	qVarReal4,
	qVarReal8,
	qVarString,
	qVarStringPtr,
	qVarReference,
	qVarArrayOfRef,
	qVarArrayOfRefPtr, 
	qVarRawData
#ifdef USE_STD_STRING
	,qVarStdString
#endif
    };

    void* operator new (size_t size);
    void  operator delete(void* p) { 
	delete[] (char*)p;
    }

    char* dump(char* buf);
    char* dumpValues(char* buf);

    dbQueryElement(ElementType t, void const* p,
		   dbTableDescriptor* table = NULL)
    {
	type = t;
	ptr  = p;
	ref  = table;
	next = NULL;
    }
  private:
    dbQueryElement*    next;
    void const*        ptr;
    ElementType        type;
    dbTableDescriptor* ref;
};


class GIGABASE_DLL_ENTRY dbQueryElementAllocator { 
    friend class dbDatabase;
    dbMutex&        mutex;
    dbQueryElement* freeChain;
    
  public:
    void free(dbQueryElement* first, dbQueryElement** lastNext) { 
	dbCriticalSection cs(mutex);
	if (first != NULL) { 
	    *lastNext = freeChain;
	    freeChain = first;
	}
    }
	
    void* allocate(size_t size) { 
	dbCriticalSection cs(mutex);
	dbQueryElement* elem = freeChain;
	if (elem != NULL) {
	    freeChain = elem->next;
	    return elem;
	} else {
	    return new byte[size];
	}
    }
    dbQueryElementAllocator();
    ~dbQueryElementAllocator();

    static dbQueryElementAllocator instance;
};

inline void* dbQueryElement::operator new(size_t size) {
    return dbQueryElementAllocator::instance.allocate(size);
}

class GIGABASE_DLL_ENTRY dbComponent {
  public:
    char_t const* structure;
    char_t const* field;

    dbComponent(char_t const* s, char_t const* f=NULL) : structure(s), field(f) {}
};

class GIGABASE_DLL_ENTRY dbQueryExpression {
    friend class dbQuery;
    dbQueryElement*  first;
    dbQueryElement** last;
    bool             operand;

    dbQueryExpression& add(dbQueryElement::ElementType type, void const* ptr) {
	last = &(*last = new dbQueryElement(type, ptr))->next;
	operand = (type == dbQueryElement::qExpression);
	return *this;
    }

  public:
    dbQueryExpression& operator = (char_t const* ptr) {
	first = NULL, last = &first;
	return add(dbQueryElement::qExpression, ptr);
    }
    dbQueryExpression& operator = (dbComponent const& comp);

    dbQueryExpression& operator = (dbQueryExpression const& expr);

    dbQueryExpression& operator,(int1& ptr) {
        return add(dbQueryElement::qVarInt1, &ptr);
    }
    dbQueryExpression& operator,(int2& ptr) {
        return add(dbQueryElement::qVarInt2, &ptr);
    }
    dbQueryExpression& operator,(int4& ptr) {
        return add(dbQueryElement::qVarInt4, &ptr);
    }
    dbQueryExpression& operator,(int8& ptr) {
        return add(dbQueryElement::qVarInt8, &ptr);
    }
    dbQueryExpression& operator,(real4& ptr) {
        return add(dbQueryElement::qVarReal4, &ptr);
    }
    dbQueryExpression& operator,(real8& ptr) {
        return add(dbQueryElement::qVarReal8, &ptr);
    }
    dbQueryExpression& operator,(void const* ptr) { 
	return add(dbQueryElement::qVarRawData, ptr);
    }
#ifndef bool
    dbQueryExpression& operator,(bool& ptr) {
        return add(dbQueryElement::qVarBool, &ptr);
    }
#endif
    dbQueryExpression& operator,(char_t const* ptr) {
        return add(operand ? dbQueryElement::qVarString
		   : dbQueryElement::qExpression, ptr);
    }
    dbQueryExpression& operator,(char_t const** ptr) {
        return add(dbQueryElement::qVarStringPtr, ptr);
    }
    dbQueryExpression& operator,(char_t** ptr) {
        return add(dbQueryElement::qVarStringPtr, ptr);
    }
#ifdef USE_STD_STRING
    dbQueryExpression& operator,(STD_STRING const& str) {
        return add(dbQueryElement::qVarStdString, &str);
    }
#endif

    dbQueryExpression& operator,(dbQueryExpression const& expr) {
	*last = new dbQueryElement(dbQueryElement::qExpression, "(");
	(*last)->next = expr.first;
	last = expr.last;
	*last = new dbQueryElement(dbQueryElement::qExpression, ")");
	last = &(*last)->next;
	operand = false;
	return *this;
    }
    dbQueryExpression& operator,(dbComponent const& comp) {
	add(dbQueryElement::qExpression, comp.structure);
	if (comp.field != NULL) {
	    add(dbQueryElement::qExpression, ".");
	    add(dbQueryElement::qExpression, comp.field);
	}
	operand = false;
	return *this;
    }
};

class dbOrderByNode;
class dbFollowByNode;

class GIGABASE_DLL_ENTRY dbCompiledQuery {
  public:
    dbExprNode*        tree;
    dbOrderByNode*     order;
    dbFollowByNode*    follow;
    dbTableDescriptor* table;

    enum IteratorInit {
	StartFromAny,
	StartFromFirst,
	StartFromLast,
	StartFromRef,
	StartFromArray,
	StartFromArrayPtr
    };
    IteratorInit       startFrom;
    void const*        root;

    void destroy();

    bool compiled() { return tree != NULL; }

    dbCompiledQuery() {
	tree = NULL;
	order = NULL;
	follow = NULL;
	table = NULL;
	startFrom = StartFromAny;
    }
};

class GIGABASE_DLL_ENTRY dbQuery : public dbCompiledQuery {
    friend class dbCompiler;
    friend class dbDatabase;
    friend class dbSubSql;
  private:
    dbMutex            mutex;
    dbQueryElement*    elements;
    dbQueryElement**   nextElement;
    bool               operand;
    bool               mutexLocked;
    //
    // Prohibite query copying
    //
    dbQuery(dbQuery const&) {}
    dbQuery& operator =(dbQuery const&) { return *this; }

  public:
    int                pos; // position of condition in statement

    char* dump(char* buf) { 
	char* p = buf;
	for (dbQueryElement* elem = elements; elem != NULL; elem = elem->next) { 
	    p = elem->dump(p);
	}
	return buf;
    }

    char* dumpValues(char* buf) { 
	char* p = buf;
	for (dbQueryElement* elem = elements; elem != NULL; elem = elem->next) { 
	    p = elem->dumpValues(p);
	}
	return buf;
    }

    dbQuery& append(dbQueryElement::ElementType type, void const* ptr,
		    dbTableDescriptor* table = NULL)
    {
	nextElement = &(*nextElement=new dbQueryElement(type,ptr,table))->next;
	operand = (type == dbQueryElement::qExpression);
	return *this;
    }

    dbQuery& reset();

    //
    // Redefined operator = and , make it possible to specify query in the
    // following way:
    //         int x, y;
    //         dbDataTime dt;
    //         dbQuery q;
    //         dbCursor<record> cursor;
    //         q = "x=",x,"and y=",y,"and",dt == "date";
    //         for (x = 0; x < max_x; x++) {
    //             for (y = 0; y < max_y; y++) {
    //                 cursor.select(q);
    //                 ...
    //             }
    //         }

    dbQuery& add(dbQueryExpression const& expr);

    dbQuery& And(char_t const* str) {
	if (elements != NULL) {
	    append(dbQueryElement::qExpression, "and");
	}
	return append(dbQueryElement::qExpression, str);
    }

    dbQuery& Or(char_t const* str) {
	if (elements != NULL) {
	    append(dbQueryElement::qExpression, "or");
	}
	return append(dbQueryElement::qExpression, str);
    }

    dbQuery& add(char_t const* str) {
	return append(operand ? dbQueryElement::qVarString
		      : dbQueryElement::qExpression, str);
    }
    dbQuery& add(char_t const** str) {
	return append(dbQueryElement::qVarStringPtr, str);
    }
    dbQuery& add(char_t** str) {
	return append(dbQueryElement::qVarStringPtr, str);
    }
#ifdef USE_STD_STRING
    dbQuery& add(STD_STRING const& str) { 
	return append(dbQueryElement::qVarStdString, &str);
    }
    dbQuery& operator,(STD_STRING const& str) { return add(str); }
#endif
    dbQuery& add(int1& value) {
	return append(dbQueryElement::qVarInt1, &value);
    }
    dbQuery& add (int2& value) {
	return append(dbQueryElement::qVarInt2, &value);
    }
    dbQuery& add (int4& value) {
	return append(dbQueryElement::qVarInt4, &value);
    }
    dbQuery& add (int8& value) {
	return append(dbQueryElement::qVarInt8, &value);
    }
    dbQuery& add (real4& value) {
	return append(dbQueryElement::qVarReal4, &value);
    }
    dbQuery& add(real8& value) {
	return append(dbQueryElement::qVarReal8, &value);
    }
    dbQuery& add(void const* value) { 
	return append(dbQueryElement::qVarRawData, value);
    }
#ifndef bool
    dbQuery& add(bool& value) {
	return append(dbQueryElement::qVarBool, &value);
    }
    dbQuery& operator,(bool&  value) { return add(value); }
#endif

    dbQuery& operator,(char_t const*  value) { return add(value); }
    dbQuery& operator,(char_t const** value) { return add(value); }
    dbQuery& operator,(char_t** value) { return add(value); }
    dbQuery& operator,(int1&  value) { return add(value); }
    dbQuery& operator,(int2&  value) { return add(value); }
    dbQuery& operator,(int4&  value) { return add(value); }
    dbQuery& operator,(int8&  value) { return add(value); }
    dbQuery& operator,(real4& value) { return add(value); }
    dbQuery& operator,(real8& value) { return add(value); }
    dbQuery& operator,(void const*  value) { return add(value); }
    dbQuery& operator,(dbQueryExpression const& expr) { return add(expr); }

    dbQuery& operator = (const char_t* str) {
	return reset().append(dbQueryElement::qExpression, str);
    }
    dbQuery& operator = (dbQueryExpression const& expr) {
	return reset().add(expr);
    }

#ifndef NO_MEMBER_TEMPLATES
    template<class T>
    inline dbQuery& operator,(dbReference<T> const& value) { 
	return append(dbQueryElement::qVarReference, &value, &T::dbDescriptor);
    }

    template<class T>
    inline dbQuery& operator,(dbArray< dbReference<T> > const& value) { 
	return append(dbQueryElement::qVarArrayOfRef, &value, 
		      &T::dbDescriptor);
    }

    template<class T>
    inline dbQuery& operator,(dbArray< dbReference<T> >const* const& value) { 
 	return append(dbQueryElement::qVarArrayOfRefPtr, &value, &T::dbDescriptor);
    }
#endif

    dbQuery() {
	elements = NULL;
	nextElement = &elements;
	operand = false;
	pos = 0;
    }
    dbQuery(char_t const* str) {
	elements = new dbQueryElement(dbQueryElement::qExpression, str);
	nextElement = &elements->next;
	operand = true;
	pos = 0;
    }
    ~dbQuery() {
	reset();
    }
};

#ifdef NO_MEMBER_TEMPLATES
template<class T>
inline dbQuery& operator,(dbQuery& query, dbReference<T> const& value) { 
    return query.append(dbQueryElement::qVarReference, &value, 
			&T::dbDescriptor);
}

template<class T>
inline dbQuery& operator,(dbQuery& query, 
			     dbArray< dbReference<T> > const& value) 
{ 
    return query.append(dbQueryElement::qVarArrayOfRef, &value, 
			&T::dbDescriptor);
}

template<class T>
inline dbQuery& operator,(dbQuery& query, 
			     dbArray< dbReference<T> >const* const& value) 
{ 
    return query.append(dbQueryElement::qVarArrayOfRefPtr, &value, 
			&T::dbDescriptor);
}
#endif

#define USER_FUNC(f) static dbUserFunction f##_descriptor(&f, #f)

class GIGABASE_DLL_ENTRY dbUserFunction {
    friend class dbDatabase;
    friend class dbCompiler;

    void*   fptr;
    char_t* name;

    dbUserFunction* next;
    static dbUserFunction* list;

    enum funcType {
	fInt2Bool,
	fReal2Bool,
	fStr2Bool,
	fInt2Int,
	fReal2Int,
	fStr2Int,
	fInt2Real,
	fReal2Real,
	fStr2Real,
	fInt2Str,
	fReal2Str,
	fStr2Str
    };
    int type;

    void bind(char_t* name, void* f, funcType ftype);

  public:

    static dbUserFunction* find(char_t const* name) {
	for (dbUserFunction* func = list; func != NULL; func = func->next) {
	    if (name == func->name) {
		return func;
	    }
	}
	return NULL;
    }

    int getParameterType();

    dbUserFunction(bool __cdecl (*f)(int8), char_t* name) {
	bind(name, (void*)f, fInt2Bool);
    }
    dbUserFunction(bool __cdecl (*f)(real8), char_t* name) {
	bind(name, (void*)f, fReal2Bool);
    }
    dbUserFunction(bool __cdecl (*f)(char_t const*), char_t* name) {
	bind(name, (void*)f, fStr2Bool);
    }
    dbUserFunction(int8 __cdecl (*f)(int8), char_t* name) {
	bind(name, (void*)f, fInt2Int);
    }
    dbUserFunction(int8 __cdecl (*f)(real8), char_t* name) {
	bind(name, (void*)f, fReal2Int);
    }
    dbUserFunction(int8 __cdecl (*f)(char_t const*), char_t* name) {
	bind(name, (void*)f, fStr2Int);
    }
    dbUserFunction(real8 __cdecl (*f)(int8), char_t* name) {
	bind(name, (void*)f, fInt2Real);
    }
    dbUserFunction(real8 __cdecl (*f)(real8), char_t* name) {
	bind(name, (void*)f, fReal2Real);
    }
    dbUserFunction(real8 __cdecl (*f)(char_t const*), char_t* name) {
	bind(name, (void*)f, fStr2Real);
    }
    dbUserFunction(char_t* __cdecl (*f)(int8), char_t* name) {
	bind(name, (void*)f, fInt2Str);
    }
    dbUserFunction(char_t* __cdecl (*f)(real8), char_t* name) {
	bind(name, (void*)f, fReal2Str);
    }
    dbUserFunction(char_t* __cdecl (*f)(char_t const*), char_t* name) {
	bind(name, (void*)f, fStr2Str);
    }
};

END_GIGABASE_NAMESPACE


#endif






