eda: Simpler approach to Id row caching by storing row directly inside the Table...
[sdk] / eda / libeda / src / EDA.ec
1 #ifdef ECERE_STATIC
2 public import static "ecere"
3 #else
4 public import "ecere"
5 #endif
6
7 #include <stdarg.h>
8
9 #ifdef _DEBUG
10 #define _DEBUG_LINE
11 #endif
12
13 public enum OpenType { queryRows, tableRows, viewRows, processesList, databasesList, tablesList, fieldsList };
14 public enum CreateOptions { no, create, readOnly };
15 public enum AccessOptions { integral, random };
16 public enum SeekOptions { none, reset, first, last, firstEqual, lastEqual };
17 public enum MoveOptions { nil, first, last, next, previous, middle, here };
18 public enum MatchOptions { nil };
19 public enum ObjectType { table, view };
20 public enum State { none, driver, connected, opened, closed, errorDriver };
21
22 #define AUTO_DELETE
23 #define AUTO_DELETE_TABLES
24
25 public class OpenOptions : uint
26 {
27 public:
28    OpenType type:8;
29    CreateOptions create:2;
30    AccessOptions access:2;
31 };
32
33 public class DataSourceDriver
34 {
35    OldList listDB { offset = (uint)&((Database)0).prev };
36    class_data char * name;
37
38    class_property char * name
39    {
40       set { class_data(name) = value; }
41       get { return class_data(name); }
42    }
43
44 public:
45    virtual String BuildLocator(DataSource ds);
46    virtual uint GetDatabasesCount();
47    virtual bool Connect(const String locator);
48    virtual void Status();
49    virtual bool RenameDatabase(const String name, const String rename);
50    virtual bool DeleteDatabase(const String name);
51    virtual Database OpenDatabase(const String name, CreateOptions create, DataSource ds);
52
53    ~DataSourceDriver()
54    {
55       Database db;
56       while((db = listDB.first))
57       {
58          listDB.Remove(db);
59          db.ds = null;
60          delete db;
61       };
62    }
63 }
64
65 static subclass(DataSourceDriver) GetDataDriver(char * driverName)
66 {
67    subclass(DataSourceDriver) driver = null;
68    driver = FindDataDriverDerivative(class(DataSourceDriver), driverName);
69    if(!driver)
70    {
71       Module module;
72       char moduleName[MAX_LOCATION];
73       sprintf(moduleName, "EDA%s", driverName);
74       if(module = eModule_Load(__thisModule.application, moduleName, publicAccess))
75          driver = FindDataDriverDerivative(eSystem_FindClass(module /*__thisModule.application*/, "DataSourceDriver"), driverName);
76    }
77    return driver;
78 }
79
80 static subclass(DataSourceDriver) FindDataDriverDerivative(Class dataSourceDriverClass, char * driverName)
81 {
82    OldLink link;
83    subclass(DataSourceDriver) derivative = null;
84    for(link = dataSourceDriverClass.derivatives.first; link; link = link.next)
85    {
86       subclass(DataSourceDriver) dataDriver = link.data;
87       Class driverClass = link.data;
88       if(dataDriver.name && !strcmp(dataDriver.name, driverName))
89       {
90          derivative = dataDriver;
91          break;
92       }
93       if(driverClass.derivatives.first && (derivative = FindDataDriverDerivative(driverClass, driverName)))
94          break;
95    }
96    return derivative;
97 }
98
99 public class DataSource
100 {
101    DataSourceDriver ds;
102    String host;
103    String port;
104    String user;
105    String pass;
106    String locator;
107    
108    ~DataSource()
109    {
110       delete locator;
111       delete ds;
112       delete host;
113       delete port;
114       delete user;
115       delete pass;
116    }
117
118 public:
119    property String driver
120    {
121       get { return ds ? ((subclass(DataSourceDriver))(ds._class)).name : null; }
122       set { delete ds; ds = value ? eInstance_New(GetDataDriver(value)) : null; }
123    }
124    property String host
125    {
126       set { delete host; host = CopyString(value); }
127       get { return host; }
128    }
129    property String port
130    {
131       set { delete port; port = CopyString(value); }
132       get { return port; }
133    }
134    property String user
135    {
136       set { delete user; user = CopyString(value); }
137       get { return user; }
138    }
139    property String pass
140    {
141       set { delete pass; pass = CopyString(value); }
142       get { return pass; }
143    }
144    property String locator
145    {
146       set
147       {
148          delete host;
149          delete port;
150          delete user;
151          delete pass;
152          delete locator;
153          locator = CopyString(value);
154       }
155       get { return locator; }
156    }
157
158    property uint databasesCount { get { return ds.GetDatabasesCount(); } }
159    bool Connect()
160    {
161       if(!locator && ds)
162       {
163          if(host || port || user || pass)
164          {
165             delete locator;
166             locator = ds.BuildLocator(this);
167          }
168       }
169       return ds ? ds.Connect(locator) : false;
170    }
171    void Status() { ds.Status(); }
172    bool RenameDatabase(const String name, const String rename) { return ds.RenameDatabase(name, rename); }
173    bool DeleteDatabase(const String name) { return ds.DeleteDatabase(name); }
174    Database OpenDatabase(const String name, CreateOptions create)
175    {
176       Database db = ds.OpenDatabase(name, create, this);
177       if(db)
178       {
179          ds.listDB.Add(db);
180          db.ds = ds;
181       }
182       return db;
183    }
184 }
185
186 public class Database
187 {
188    Database prev, next;
189    DataSourceDriver ds;
190    OldList listTbl { offset = (uint)&((Table)0).prev };
191    public virtual String GetName();
192
193    ~Database()
194    {
195       Table tbl;
196       if(ds)
197          ds.listDB.Remove(this);
198       while((tbl = listTbl.first))
199       {
200          listTbl.Remove(tbl);
201          tbl.db = null;
202          delete tbl;
203       };
204    }
205    public void LinkTable(Table tbl)
206    {
207 #ifdef AUTO_DELETE_TABLES
208       listTbl.Add(tbl);
209 #endif
210       tbl.db = this;
211    }
212
213 public:
214    property String name { get { return GetName(); } }
215    property uint tablesCount { get { return ObjectsCount(table); } }
216    property uint viewsCount { get { return ObjectsCount(view); } }
217
218    virtual uint ObjectsCount(ObjectType type);
219    virtual bool RenameObject(ObjectType type, const String name, const String rename);
220    virtual bool DeleteObject(ObjectType type, const String name);
221    virtual Table OpenTable(const String name, OpenOptions open);
222    virtual bool Begin();
223    virtual bool Commit();
224    virtual bool CreateCustomFunction(char * name, SQLCustomFunction customFunction);
225 }
226
227 public enum IndexOrder { ascending, descending };
228
229 public struct FieldIndex
230 {
231    Field field;
232    IndexOrder order;
233    Field memberField;
234    Table memberTable;
235    Field memberIdField;
236 };
237
238 Mutex idRowCacheMutex { };
239
240 public class Table
241 {
242    class_no_expansion;
243    Table prev, next;
244    Database db;
245    OldList listRows { offset = (uint)&((Row)0).prev };
246    Row cachedIdRow;
247 public:
248    virtual String GetName();
249    virtual Field GetFirstField();
250    virtual uint GetFieldsCount();
251    virtual uint GetRowsCount();
252    virtual DriverRow CreateRow();
253
254    ~Table()
255    {
256       Row row;
257
258       // Delete cached Id row
259       idRowCacheMutex.Wait();
260       delete cachedIdRow;
261       idRowCacheMutex.Release();
262
263 #ifdef AUTO_DELETE_TABLES
264       if(db)
265          db.listTbl.Remove(this);
266 #endif
267       while((row = listRows.first))
268          row.tbl = null;
269    }
270 public:
271    property String name { get { return GetName(); } }
272    property Field firstField { get { return GetFirstField(); } }
273    property uint fieldsCount { get { return GetFieldsCount(); } }
274    property uint rowsCount { get { return GetRowsCount(); } }
275    
276    virtual Field AddField(const String name, Class type, int length);
277    virtual Field FindField(const String name);
278    virtual bool GenerateIndex(int count, FieldIndex * fieldIndexes, bool init);
279    bool Index(int count, FieldIndex * fieldIndexes)
280    {
281       return GenerateIndex(count, fieldIndexes, true);
282    }
283
284    void GUIListBoxAddRowsField(ListBox list, const String fieldName)
285    {
286       Field fld;
287       list.Clear();
288       fld = FindField(fieldName);
289       if(fld)
290       {
291          Row r { this };
292          while(r.Next())
293          {
294             String s/* = null*/;
295             r.GetData(fld, s);
296             list.AddRow().SetData(null, s);
297             delete s;
298          }
299          delete r;
300       }
301    }
302
303    void GUIListBoxAddFields(ListBox list)
304    {
305       Field fld;
306       list.Clear();
307       list.ClearFields();
308       for(fld = firstField; fld; fld = fld.next)
309       {
310          DataField df
311          {
312             alignment = left;
313             dataType = fld.type;
314             editable = true;
315             header = fld.name;
316             width = 100;
317          };         
318          list.AddField(df);
319       }
320    }
321
322    void GUIListBoxAddRows(ListBox list)
323    {
324       list.Clear();
325       if(rowsCount)
326       {
327          DataField df;
328          DataRow dr;
329          Row r { this };
330          dr = list.firstRow;
331          while(r.Next())
332          {
333             Field fld = firstField;
334             for(df = list.firstField; df; df = df.next, fld = fld.next)
335             {
336                int64 data = 0;
337                Class type = fld.type;
338                if(type.type == unitClass && !type.typeSize)
339                {
340                   Class dataType = eSystem_FindClass(type.module, type.dataTypeString);
341                   if(dataType)
342                      type = dataType;
343                }
344                if(type.type == structClass)
345                   data = (int64)new0 byte[type.structSize];
346                if(!df.prev)
347                {
348                   dr = list.AddRow();
349                   dr.tag = r.sysID;
350                }
351                ((bool (*)())(void *)r.GetData)(r, fld, type, (type.type == structClass) ? (void *)data : &data);
352                if(type.type == systemClass || type.type == unitClass || type.type == bitClass || type.type == enumClass)
353                   dr.SetData(df, (void *)&data);
354                else
355                   dr.SetData(df, (void *)data);
356
357                // Is this missing some frees here? strings? Probably not: freeData = true?
358                // type._vTbl[__ecereVMethodID_class_OnFree](type, data);
359                if(type.type == structClass)
360                {
361                   void * dataPtr = (void *)data;
362                   delete dataPtr;
363                }
364                else if(!strcmp(type.dataTypeString, "char *"))
365                {
366                   // Strings are handled as a special case in ListBox -- normalClass, but copied when freeData = true
367                   char * string = (char *)data;
368                   delete string;
369                }
370             }
371             dr = dr.next;
372          }
373          delete r;
374       }
375    }
376 }
377
378 public class Field
379 {
380    class_no_expansion
381 public:
382    virtual String GetName();
383    virtual Class GetType();
384    virtual int GetLength();
385    virtual Field GetPrev();
386    virtual Field GetNext();
387    virtual Table GetTable();
388
389    property String name { get { return GetName(); } }
390    property Class type { get { return GetType(); } }
391    property int length { get { return GetLength(); } }
392    property Field prev { get { return GetPrev(); } }
393    property Field next { get { return GetNext(); } }
394    property Table table { get { return GetTable(); } }
395
396    bool GetData(Row row, typed_object & data)
397    {
398       return row.GetData(this, data);
399    }
400
401    bool SetData(Row row, typed_object & data)
402    {
403       return row.SetData(this, data);
404    }
405 };
406
407 public class Row
408 {
409    class_no_expansion;
410    DriverRow row;
411    Row prev, next;
412    Table tbl;
413
414    ~Row()
415    {
416 #ifdef AUTO_DELETE
417       if(tbl)
418          tbl.listRows.Remove(this);
419 #endif
420       delete row;
421    }
422
423 public:
424    property Table tbl
425    {
426       set
427       {
428          delete row;
429          row = value ? value.CreateRow() : null;
430          if(tbl)
431          {
432 #ifdef AUTO_DELETE
433             tbl.listRows.Remove(this);
434 #endif
435             tbl = value;
436             if(!value)
437             {
438                delete this;
439                return;
440             }
441          }
442          if(value)
443          {
444 #ifdef AUTO_DELETE
445             if(!tbl)
446                incref this;
447 #endif
448             tbl = value;
449 #ifdef AUTO_DELETE
450             tbl.listRows.Add(this);
451 #endif
452          }         
453       }
454       get { return tbl; }
455    }
456
457    property bool nil { get { return row ? row.Nil() : true; } }
458
459    property char * query { set { if(row) row.Query(value); } }
460
461    public bool Query(char * query)  // Add printf format support
462    {
463       if(row)
464          return row.Query(query);
465       return false;
466    }
467    bool Select(MoveOptions move) { return row ? row.Select(move) : false; }
468    bool First() { return row ? row.Select(first) : false; }
469    bool Last() { return row ? row.Select(last) : false; }
470    bool Next() { return row ? row.Select(next) : false; }
471    bool Previous() { return row ? row.Select(previous) : false; }
472
473    bool Find(Field field, MoveOptions move, MatchOptions match, typed_object data) { return (row && field) ? row.Find(field, move, match, data) : false; }
474    bool FindMultiple(FieldFindData * findData, MoveOptions move, int numFields) { return row ? row.FindMultiple(findData, move, numFields) : false; }
475
476    bool Synch(Row to) { return row && to && row._class == to.row._class ? row.Synch(to.row) : false; }
477
478    bool Add() { return row ? row.Add(0) : false; }
479    bool AddID(uint64 id) { return row ? row.Add(id) : false; }
480    bool GetData(Field field, typed_object & data) { return (row && field) ? row.GetData(field, data) : false; }
481    bool SetData(Field field, typed_object data) { return (row && field) ? row.SetData(field, data) : false; }
482    bool Delete() { return row ? row.Delete() : false; }
483    // TODO: Implement as a typed_object?
484    bool SetQueryParam(int paramID, int value) { return row ? row.SetQueryParam(paramID, value) : false; }
485    bool SetQueryParam64(int paramID, int64 value) { return row ? row.SetQueryParam64(paramID, value) : false; }   
486    bool SetQueryParamText(int paramID, char * value) { return row ? row.SetQueryParamText(paramID, value) : false; }
487    bool SetQueryParamObject(int paramID, void * value, Class type) { return row ? row.SetQueryParamObject(paramID, value, type) : false; }
488    char * GetColumn(int paramID) { return row ? row.GetColumn(paramID) : null; }
489
490    bool GUIDataRowSetData(DataRow dr, DataField df, Field fld)
491    {
492       int64 data = 0;
493       Class type = fld.type;
494       if(type.type == unitClass && !type.typeSize)
495       {
496          Class dataType = eSystem_FindClass(type.module, type.dataTypeString);
497          if(dataType)
498             type = dataType;
499       }
500       if(type.type == structClass)
501          data = (int64)new0 byte[type.structSize];
502       ((bool (*)())(void *)GetData)(this, fld, type, (type.type == structClass) ? (void *)data : &data);
503
504       if((type.type == systemClass || type.type == unitClass || type.type == bitClass || type.type == enumClass))
505          dr.SetData(df, (void *)&data);
506       else
507          dr.SetData(df, (void *)data);
508       if(type.type == structClass)
509       {
510          void * dataPtr = (void *)data;
511          delete dataPtr;
512       }
513       else if(!strcmp(type.dataTypeString, "char *"))
514       {
515          // Strings are handled as a special case in ListBox -- normalClass, but copied when freeData = true
516          char * string = (char *)data;
517          delete string;
518       }
519    }
520
521    property uint sysID { get { return row ? row.GetSysID() : 0; } set { if(row) row.GoToSysID(value); } }
522 };
523
524 public class DriverRow
525 {
526 public:
527    virtual bool Nil();
528    virtual bool Select(MoveOptions move);
529    virtual bool Find(Field fld, MoveOptions move, MatchOptions match, typed_object data);
530    virtual bool FindMultiple(FieldFindData * findData, MoveOptions move, int numFields);
531    virtual bool Synch(DriverRow to);
532    virtual bool Add(uint64 id);
533    virtual bool Delete();
534
535    virtual bool GetData(Field fld, typed_object &data);
536    virtual bool SetData(Field fld, typed_object data);
537    virtual uint GetSysID();
538    virtual bool GoToSysID(uint id);
539    virtual bool Query(char * queryString);
540    virtual bool SetQueryParam(int paramID, int value);
541    virtual bool SetQueryParam64(int paramID, int64 value);
542    virtual bool SetQueryParamText(int paramID, char * value);
543    virtual bool SetQueryParamObject(int paramID, void * data, Class type);
544    virtual char * GetColumn(int paramID);
545 };
546
547 public class SQLCustomFunction
548 {
549 public:
550    Array<char> array { minAllocSize = 1024 };
551    virtual void Process(char * text);
552 }
553
554 public struct FieldFindData
555 {
556    Field field;
557    DataValue value;
558 };
559
560 static inline void DebugLn(typed_object object, ...)
561 {
562 #if defined(_DEBUG_LINE)
563    va_list args;
564    char buffer[4096];
565    va_start(args, object);
566    PrintStdArgsToBuffer(buffer, sizeof(buffer), object, args);
567    va_end(args);
568    puts(buffer);
569 #endif
570 }