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