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