home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 June / Chip_2002-06_cd1.bin / zkuste / delphi / kompon / d456 / JANSQL.ZIP / janSQLDemo / components / janSQL.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2002-03-31  |  79.4 KB  |  3,094 lines

  1. {-----------------------------------------------------------------------------
  2. The contents of this file are subject to the Mozilla Public License Version
  3. 1.1 (the "License"); you may not use this file except in compliance with the
  4. License. You may obtain a copy of the License at
  5. http://www.mozilla.org/NPL/NPL-1_1Final.html
  6.  
  7. Software distributed under the License is distributed on an "AS IS" basis,
  8. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  9. the specific language governing rights and limitations under the License.
  10.  
  11. The Original Code is: janSQL.pas, released March 24, 2002.
  12.  
  13. The Initial Developer of the Original Code is Jan Verhoeven
  14. (jan1.verhoeven@wxs.nl or http://jansfreeware.com).
  15. Portions created by Jan Verhoeven are Copyright (C) 2002 Jan Verhoeven.
  16. All Rights Reserved.
  17.  
  18. Contributor(s): ___________________.
  19.  
  20. Last Modified: 25-mar-2002
  21. Current Version: 1.1
  22.  
  23. Notes: This is a very fast single user SQL engine for text based tables
  24.  
  25. Known Issues:
  26.  
  27.  
  28. History:
  29.   1.1 25-mar-2002
  30.       release recordset in subquery
  31.       release sqloutput in selectfromjoin
  32.       allow "unlimited" number of tables in join
  33.       allow calculated updates: set field=expression
  34.       modified TjanSQLRecord: fields are now objects (for future enhancements)
  35.   1.0 24-mar-2002 : original release
  36. -----------------------------------------------------------------------------}
  37.  
  38.  
  39. unit janSQL;
  40.  
  41. interface
  42.  
  43. uses
  44.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, FileCtrl,Dialogs,janSQLstrings,janSQLExpression2,janSQLTokenizer, mwStringHashList;
  45.  
  46. type
  47.   TCompareProc     = procedure( Sender : TObject; i, j : Integer; var Result : Integer ) of object ;
  48.   TSwapProc     = procedure( Sender : TObject; i, j : Integer ) of object ;
  49.  
  50.   TjanSQLOperator=(jsunknown,jseq,jsne,jsgt,jsge,jslt,jsle);
  51.   TjanSQLBoolean=(jsnone,jsand,jsor);
  52.  
  53.   TjanSQLSort=record
  54.     FieldIndex:integer;
  55.     SortAscending:boolean;
  56.     SortNumeric:boolean;
  57.   end;
  58.  
  59.   TjanSQLJoinIterator=record
  60.     TableName:string;
  61.     TableAlias:string;
  62.     RecordSetIndex:integer;
  63.     RecordCount:integer;
  64.     CurrentRecord:integer;
  65.   end;
  66.  
  67.   TjanSQLCalcField=class(TObject)
  68.   private
  69.     FCalc:TjanSQLExpression2;
  70.     Fexpression: string;
  71.     Fname: string;
  72.     FFieldIndex: integer;
  73.     procedure Setexpression(const Value: string);
  74.     procedure Setname(const Value: string);
  75.     function getValue: variant;
  76.     procedure SetFieldIndex(const Value: integer);
  77.   public
  78.     constructor create;
  79.     destructor  destroy;override;
  80.     property expression:string read Fexpression write Setexpression;
  81.     property name:string read Fname write Setname;
  82.     property value:variant read getValue;
  83.     property Calculator:TjanSQLExpression2 read FCalc;
  84.     property FieldIndex:integer read FFieldIndex write SetFieldIndex;
  85.   end;
  86.  
  87.   TjanSQLOutput=class(TObject)
  88.   private
  89.     FFields:TList;
  90.     function getFieldCount: integer;
  91.     function getField(index: integer): TjanSQLCalcField;
  92.     function getFieldNames: string;
  93.   public
  94.     constructor create;
  95.     destructor  destroy;override;
  96.     procedure ClearFields;
  97.     function  AddField:TjanSQLCalcField;
  98.     property FieldNames:string read getFieldNames;
  99.     property FieldCount:integer read getFieldCount;
  100.     property Fields[index:integer]:TjanSQLCalcField read getField;
  101.   end;
  102.  
  103.   TjanSQLField=record
  104.     FieldName:string;
  105.     FieldIndex:integer;
  106.     FieldValue:string;
  107.   end;
  108.  
  109.   TjanSQLFields=array of TjanSQLField;
  110.  
  111.   TjanSQLRecordField=class(TObject)
  112.   private
  113.     Fsum: double;
  114.     Fsum2: double;
  115.     Fcount: integer;
  116.     Fvalue: variant;
  117.     procedure Setcount(const Value: integer);
  118.     procedure Setsum(const Value: double);
  119.     procedure Setsum2(const Value: double);
  120.     procedure Setvalue(const Value: variant);
  121.   published
  122.     property value:variant read Fvalue write Setvalue;
  123.     property count:integer read Fcount write Setcount;
  124.     property sum:double read Fsum write Setsum;
  125.     property sum2:double read Fsum2 write Setsum2;
  126.   end;
  127.  
  128.  
  129.   TjanRecord=class(TObject)
  130.   private
  131.     FFields:TList;
  132.     Fmark: boolean;
  133.     Fcounter: integer;
  134.     function getrow: string;
  135.     procedure setrow(const Value: string);
  136.     function getfield(index: integer): TjanSQLRecordField;
  137.     procedure setfield(index: integer; const Value: string);
  138.     procedure Setmark(const Value: boolean);
  139.     procedure Setcounter(const Value: integer);
  140.     procedure ClearFields;
  141.   public
  142.     constructor create;
  143.     destructor  destroy; override;
  144.     procedure AddField(value:string);
  145.     function DeleteField(index:integer):boolean;
  146.     property row:string read getrow write setrow;
  147.     property fields[index:integer]:TjanSQLRecordField read getfield;
  148.     property mark:boolean read Fmark write Setmark;
  149.     property counter:integer read Fcounter write Setcounter;
  150.   end;
  151.  
  152.   TjanRecordList=class(TList)
  153.   private
  154.   public
  155.     destructor  destroy; override;
  156.     procedure   Clear; override;
  157.     procedure  delete(index:integer);
  158.   end;
  159.  
  160.   TjanRecordSetList=class(TStringList)
  161.   public
  162.     destructor  destroy; override;
  163.     procedure delete(index:integer);override;
  164.   end;
  165.  
  166.   TjanRecordSet=class(TObject)
  167.   private
  168.     FRecordCursor:integer;
  169.     Fname: string;
  170.     FFieldNames:TStringList;
  171.     FFieldFuncs:array of TTokenOperator;
  172.     FRecords:TjanRecordList;
  173.     Fpersistent: boolean;
  174.     Fmodified: boolean;
  175.     Fmatchrecord: integer;
  176.     Falias: string;
  177.     Fintermediate: boolean;
  178.     procedure Setname(const Value: string);
  179.     function getrecord(index: integer): TjanRecord;
  180.     function getfieldvalue(index: variant): string;
  181.     procedure setfieldvalue(index: variant; const Value: string);
  182.     procedure Setpersistent(const Value: boolean);
  183.     function getrecordcount: integer;
  184.     procedure Setmodified(const Value: boolean);
  185.     function getfieldcount: integer;
  186.     procedure Setmatchrecord(const Value: integer);
  187.     function getLongFieldList: string;
  188.     function getShortFieldList: string;
  189.     procedure Setalias(const Value: string);
  190.     procedure Setintermediate(const Value: boolean);
  191.   public
  192.     constructor create;
  193.     destructor  destroy; override;
  194.     function LoadFromFile(filename:string):boolean;
  195.     function SaveToFile(filename:string):boolean;
  196.     function AddRecord:integer;
  197.     function DeleteRecord(index:integer):boolean;
  198.     function AddField(fieldname,value:string):integer;
  199.     function DeleteField(index:variant):integer;
  200.     function IndexOfField(fieldname:string):integer;
  201.     function FindFieldValue(fieldindex:integer;fieldvalue:string):integer;
  202.     procedure Clear;
  203.     property name:string read Fname write Setname;
  204.     property alias:string read Falias write Setalias;
  205.     property persistent:boolean read Fpersistent write Setpersistent;
  206.     property intermediate:boolean read Fintermediate write Setintermediate;
  207.     property modified:boolean read Fmodified write Setmodified;
  208.     property FieldNames:TStringList read FFieldNames;
  209.     property ShortFieldList:string read getShortFieldList;
  210.     property LongFieldList:string read getLongFieldList;
  211.     property records[index:integer]:TjanRecord read getrecord;
  212.     property fields[index:variant]:string read getfieldvalue write setfieldvalue;
  213.     property recordcount:integer read getrecordcount;
  214.     property fieldcount:integer read getfieldcount;
  215.     property matchrecord:integer read Fmatchrecord write Setmatchrecord;
  216.   end;
  217.  
  218.   TjanSQL=class;
  219.  
  220.   TjanSQLQuery=class(TObject)
  221.   private
  222.     FTokens:TList;
  223.     FParser:TjanSQLExpression2;
  224.     FEngine: TjanSQL;
  225.     procedure SetEngine(const Value: TjanSQL);
  226.     procedure ClearTokenList;
  227.     function GetToken(index: integer): TToken;
  228.     function getParser: TjanSQLExpression2;
  229.   protected
  230.   public
  231.     constructor create;
  232.     destructor  destroy;override;
  233.     property Engine:TjanSQL read FEngine write SetEngine;
  234.     property Tokens[index:integer]:TToken read GetToken;
  235.     property Parser:TjanSQLExpression2 read getParser;
  236.   end;
  237.  
  238.   TjanSQL = class(TObject)
  239.   private
  240.     FQueries:TList;
  241.     gen:TStringList;
  242.     FSQL:TstringList;
  243.     FEparser:TjanSQLExpression2;
  244.     FNameSpace:TmwStringHashList;
  245.     FNameCounter:integer;
  246.     Fcatalog: string;
  247.     FRecordSets:TjanRecordSetList;
  248.     FMatchrecordSet: integer;
  249.     FMatchingHaving: boolean;
  250.     function getrecordset(index: integer): TjanRecordSet;
  251.     function getRecordSetCount: integer;
  252.     procedure getvariable(sender:Tobject;const VariableName:string;var VariableValue:variant;var handled:boolean);
  253.     procedure procsubexpression(sender:Tobject;const subexpression:string;var subexpressionValue:variant;var handled:boolean);
  254.     function SQLDirectStatement(query:TjanSQLQuery;value: string): integer;
  255.     procedure Sort(aRecordset,From, Count : Integer;orderby:array of TjanSQLSort);
  256.     procedure SortRecordSet(arecordset,From, Count : Integer;orderbylist:string;ascending:boolean);
  257.     procedure GroupBy(arecordset:TjanRecordset;grouplist:string);
  258.     function  Compare(arecordset,i, j : Integer;orderby:array of TjanSQLSort): Integer;
  259.     procedure Swap(arecordset,i,j:Integer);
  260.     procedure ClearQueries;
  261.     function ISQL(value:string):integer;
  262.     function uniqueName:string;
  263.     function addRecordSet(aname:string):integer;
  264.     function CreateTable(tablename,fields:string):integer;
  265.     function DropTable(tablename:string):integer;
  266.     function SaveTable(tablename:string):integer;
  267.     function ReleaseTable(tablename:string):integer;
  268.     function AddTableColumn(tablename,column,value:string):integer;
  269.     function DropTableColumn(tablename,column:string):integer;
  270.     function IndexOfTable(tablename:string):integer;
  271.     function openTable(value:string;persistent:boolean):boolean;
  272.     function InCatalog(value:string):boolean;
  273.     function openCatalog(value:string):integer;
  274.     function SelectFromJoin(query:TjanSQLQuery;selectfields,tablelist,wherecondition,orderbylist:string;ascending:boolean;wfrom,wtill:integer;grouplist,having,resultname:string):integer;
  275.     function SelectFrom(query:TjanSQLQuery;tablename1,selectfields,wherecondition,orderbylist:string;ascending:boolean;wfrom,wtill:integer;grouplist,having,resultname:string):integer;
  276.     function DeleteFrom(tablename1,wherecondition:string):integer;
  277.     function InsertInto(tablename1,columns,values:string):integer;
  278.     function Update(query:TjanSQLQuery;tablename1,updatelist,wherecondition:string):integer;
  279.     function Commit(query:TjanSQLQuery):integer;
  280.     function AddQuery:TjanSQLQuery;
  281.     function DeleteQuery(query:TjanSQLQuery):boolean;
  282.     { Private declarations }
  283.   protected
  284.     { Protected declarations }
  285.     function SQLSelect(query:TjanSQLQuery;aline,aname:string):integer;
  286.     function SQLAssign(query:TjanSQLQuery;aline:string):integer;
  287.     function SQLDelete(query:TjanSQLQuery;aline:string):integer;
  288.     function SQLInsert(query:TjanSQLQuery;aline:string):integer;
  289.     function SQLInsertSelect(query:TjanSQLQuery;aline:string):integer;
  290.     function SQLUpdate(query:TjanSQLQuery;aline:string):integer;
  291.     function SQLCreate(query:TjanSQLQuery;aline:string):integer;
  292.     function SQLDrop(query:TjanSQLQuery;aline:string):integer;
  293.     function SQLSaveTable(query:TjanSQLQuery;aline:string):integer;
  294.     function SQLReleaseTable(query:TjanSQLQuery;aline:string):integer;
  295.     function SQLAlter(query:TjanSQLQuery;aline:string):integer;
  296.     function SQLCommit(query:TjanSQLQuery;aline:string):integer;
  297.     function SQLConnect(query:TjanSQLQuery;aline:string):integer;
  298.   public
  299.     { Public declarations }
  300.     constructor create;
  301.     destructor  destroy; override;
  302.     function SQLDirect(value:string):integer;
  303.     function ReleaseRecordset(arecordset:integer):boolean;
  304.     function Error:string;
  305.     property RecordSets[index:integer]:TjanRecordSet read getrecordset;
  306.     property RecordSetCount:integer read getRecordSetCount;
  307.     property NameSpace:TmwStringHashList read FNameSpace;
  308.   published
  309.     { Published declarations }
  310.   end;
  311.  
  312.  
  313. implementation
  314.  
  315. const
  316.   cr = chr(13)+chr(10);
  317.   tab = chr(9);
  318.  
  319. var
  320.   FError: string;
  321.   FDebug: string;
  322. procedure err(value:string);
  323. begin
  324.   Ferror:=value;
  325. end;
  326.  
  327. procedure chop(var value:string;from:integer);
  328. begin
  329.   value:=trim(copy(value,from,maxint));
  330. end;
  331.  
  332. function tokeninset(token,aset:string):boolean;
  333. begin
  334. end;
  335.  
  336. function parsetoken(const source:string;var token:string;var delimpos:integer;var delim:string):boolean;
  337. var
  338.   p,L:integer;
  339. begin
  340.   result:=false;
  341.   L:=length(source);
  342.   if L=0 then exit;
  343.   p:=1;
  344.   while (p<=L) and (not (source[p] in [',',' ',';','=','<','>','(',')'])) do
  345.     inc(p);
  346.   if p>L then begin
  347.     token:=source;
  348.     delim:='';
  349.     delimpos:=0;
  350.   end
  351.   else begin
  352.     token:=copy(source,1,p-1);
  353.     delim:=copy(source,p,1);
  354.     delimpos:=p;
  355.   end;
  356.   result:=true;
  357. end;
  358.  
  359. function checktoken(source,token:string;var delimpos:integer;var delim:string):boolean;
  360. var
  361.   p,LS,LT:integer;
  362. begin
  363.   result:=false;
  364.   p:=postext(token,source);
  365.   if p<>1 then exit;
  366.   LS:=length(source);
  367.   LT:=length(token);
  368.   if LS=LT then begin
  369.     delim:='';
  370.     delimpos:=0;
  371.     result:=true;
  372.     exit;
  373.   end;
  374.   if not (source[LT+1] in [' ',',',';','=','<','>']) then exit;
  375.   result:=true;
  376.   delim:=source[LT+1];
  377.   delimpos:=LT+1;
  378. end;
  379.  
  380. function string2operator(value:string):TjanSQLOperator;
  381. begin
  382.   result:=jsunknown;
  383.   if value='=' then
  384.     result:=jseq
  385.   else if value='<>' then
  386.     result:=jsne
  387.   else if value='>' then
  388.     result:=jsgt
  389.   else if value='>=' then
  390.     result:=jsge
  391.   else if value='<' then
  392.     result:=jslt
  393.   else if value='<+' then
  394.     result:=jsle;
  395. end;
  396.  
  397. // split atext at ; into lines
  398. procedure split(atext:string;alist:TStrings);
  399. var
  400.   tmp:string;
  401.   p1,p2,L:integer;
  402. begin
  403.   alist.Clear;
  404.   L:=length(atext);
  405.   if L=0 then exit;
  406.   p1:=1;
  407.   repeat
  408.     p2:=PosStr(';',atext,p1);
  409.     if p2>0 then begin
  410.       tmp:=copy(atext,p1,p2-p1);
  411.       alist.Append(tmp);
  412.       if p2=L then
  413.         alist.append('');
  414.       p1:=p2+1;
  415.       if p1>L then
  416.         p1:=0;
  417.     end
  418.     else begin
  419.       alist.append(copy(atext,p1,maxint));
  420.       p1:=0;
  421.     end;
  422.   until p1=0;
  423. end;
  424.  
  425. function join(alist:TStrings):string;
  426. var
  427.   i,c:integer;
  428. begin
  429.   result:='';
  430.   c:=alist.count;
  431.   if c=0 then exit;
  432.   for i:=0 to c-1 do
  433.     if i=0 then
  434.       result:=alist[i]
  435.     else
  436.       result:=result+';'+alist[i];
  437. end;
  438.  
  439.  
  440. { TjanSQL }
  441.  
  442. function TjanSQL.addRecordSet(aname: string): integer;
  443. var
  444.   rs:TjanRecordSet;
  445. begin
  446.   rs:=TjanRecordSet.create;
  447.   rs.name:=aname;
  448.   result:=FRecordSets.AddObject(aname,rs)+1;
  449. end;
  450.  
  451. constructor TjanSQL.create;
  452. begin
  453.   DecimalSeparator:='.';
  454.   FQueries:=TList.create;
  455.   gen:=TStringList.create;
  456.   FSQL:=TStringList.create;
  457.   FNameSpace:=TmwStringHashList.create(tinyhash,HashSecondaryOne,HashCompare);
  458.   FEParser:=TjanSQLExpression2.create;
  459.   FEParser.onGetVariable:=getvariable;
  460.   FrecordSets:=TjanRecordSetList.Create;
  461. end;
  462.  
  463. destructor TjanSQL.destroy;
  464. begin
  465.   ClearQueries;
  466.   FQueries.free;
  467.   gen.free;
  468.   FSQL.free;
  469.   FEParser.free;
  470.   FrecordSets.free;
  471.   FNameSpace.free;
  472.   inherited;
  473. end;
  474.  
  475. // join 2 tables on fields in fieldset
  476. // return index of resultset
  477. // result -1 means failure
  478. // fieldset has format field1=field2;field3=field3
  479. function TjanSQL.getrecordset(index: integer): TjanRecordSet;
  480. // 1 based
  481. begin
  482.   result:=nil;
  483.   if (index<1) or (index>Frecordsets.Count) then exit;
  484.   result:=TjanRecordset(FRecordsets.objects[index-1]);
  485. end;
  486.  
  487. // joinfields are of format field1=field2;field3=field4
  488. // all fields must be in table.field format
  489. function TjanSQL.selectFromJoin(query:TjanSQLQuery;selectfields,tablelist,wherecondition,orderbylist:string;ascending:boolean;wfrom,wtill:integer;grouplist,having,resultname:string):integer;
  490. var
  491.   t1,t2,t3:integer;
  492.   i,c,i1,c1,i2,c2,i3,c3,ij:integer;
  493.   idx:integer;
  494.   bAggregate:boolean;
  495.   selectcount,joincount:integer;
  496.   fieldname,fieldvalue:string;
  497.   displayfield,displayfields:string;
  498.   outputfieldcount:integer;
  499.   joinfieldindexes:array of array of integer;
  500.   selectfieldindexes:array of array of integer;
  501.   selectfieldfunctions:array of TTokenOperator;
  502.   m2:integer;
  503.   tablename1,tablename2:string;
  504.   tablecount:integer;
  505.   sqloutput:TjanSQLOutput;
  506.   tables:array of TjanSQLJoinIterator;
  507.   deb:integer;
  508.  
  509.   function setgroupfunc(avalue:string;ii:integer):string;
  510.   var
  511.     ppo,ppc:integer;
  512.     sfun:string;
  513.   begin
  514.     selectfieldfunctions[ii]:=toNone;
  515.     result:=avalue;
  516.     ppo:=posstr('(',avalue);
  517.     if ppo>0 then begin
  518.       ppc:=posstr(')',avalue,ppo);
  519.       if ppc=0 then exit;
  520.       sfun:=lowercase(trim(copy(avalue,1,ppo-1)));
  521.       result:=copy(avalue,ppo+1,ppc-ppo-1);
  522.       if sfun='count' then begin
  523.         selectfieldfunctions[ii]:=tosqlCount;
  524.         bAggregate:=true;
  525.       end
  526.       else if sfun='sum' then begin
  527.         selectfieldfunctions[ii]:=tosqlSum;
  528.         bAggregate:=true;
  529.       end
  530.       else if sfun='avg' then begin
  531.         selectfieldfunctions[ii]:=tosqlAvg;
  532.         bAggregate:=true;
  533.       end
  534.       else if sfun='max' then begin
  535.         selectfieldfunctions[ii]:=tosqlMax;
  536.         bAggregate:=true;
  537.       end
  538.       else if sfun='min' then begin
  539.         selectfieldfunctions[ii]:=tosqlMin;
  540.         bAggregate:=true;
  541.       end
  542.  
  543.         else
  544.           result:=avalue;
  545.     end;
  546.   end;
  547.  
  548.  
  549.   function setoutputfields:boolean;
  550.   var
  551.     ii,cc:integer;
  552.     ofld:TjanSQLCalcField;
  553.     ppa:integer;
  554.     sfield,salias,prefield:string;
  555.   begin
  556.     result:=false;
  557.     split(selectfields,gen);
  558.     cc:=gen.count;
  559.     outputfieldcount:=cc;
  560.     setlength(selectfieldfunctions,cc);
  561.     sqloutput:=TjanSQLOutput.create;
  562.     for ii:=0 to cc-1 do begin
  563.       ofld:=sqloutput.AddField;
  564.       ofld.Calculator.onGetVariable:=GetVariable;
  565.       sfield:=gen[ii];
  566.       ppa:=pos('|',sfield);
  567.       if ppa>0 then begin
  568.         prefield:=copy(sfield,1,ppa-1);
  569.         prefield:=setgroupfunc(prefield,ii);
  570.         ofld.name:=copy(sfield,ppa+1,maxint);
  571.         try
  572.           ofld.expression:=prefield;
  573.         except
  574.           exit;
  575.         end;
  576.       end
  577.       else begin
  578.         ofld.name:=setgroupfunc(sfield,ii);
  579.         try
  580.           ofld.expression:=sfield;
  581.         except
  582.           exit;
  583.         end;
  584.       end;
  585.     end;
  586.     result:=true;
  587.   end;
  588.  
  589.  
  590.  
  591.   procedure addresultoutput(r1,r2:integer);
  592.   var
  593.     ii,cc,ir:integer;
  594.     ss:string;
  595.     newr:TjanRecord;
  596.     v:variant;
  597.   begin
  598.     ir:=recordsets[t3].AddRecord;
  599.     cc:=sqloutput.FieldCount;
  600.     FMatchrecordSet:=t1;
  601.     recordsets[t1].matchrecord:=r1;
  602.     recordsets[t2].matchrecord:=r2;
  603.     for ii:=0 to cc-1 do begin
  604.        v:=sqloutput.Fields[ii].value;
  605.        ss:=v;
  606.        recordsets[t3].records[ir].fields[ii].value:=ss;
  607.     end;
  608.   end;
  609.  
  610.   procedure addresultoutputEx;
  611.   var
  612.     ii,cc,ir:integer;
  613.     ss:string;
  614.     newr:TjanRecord;
  615.     v:variant;
  616.   begin
  617.     ir:=recordsets[t3].AddRecord;
  618.     cc:=sqloutput.FieldCount;
  619.     FMatchrecordSet:=t1;
  620.     for ii:=0 to tablecount-1 do
  621.       recordsets[tables[ii].RecordSetIndex].matchrecord:=tables[ii].CurrentRecord;
  622.     for ii:=0 to cc-1 do begin
  623.        v:=sqloutput.Fields[ii].value;
  624.        ss:=v;
  625.        recordsets[t3].records[ir].fields[ii].value:=ss;
  626.     end;
  627.   end;
  628.  
  629.  
  630.  
  631.   function matchrecords(r1,r2:integer):boolean;
  632.   begin
  633.     recordsets[t1].matchrecord:=r1;
  634.     recordsets[t2].matchrecord:=r2;
  635.     result:=query.parser.Evaluate;
  636.   end;
  637.  
  638.   function matchrecordsEx:boolean;
  639.   var
  640.     ii:integer;
  641.   begin
  642.     for ii:=0 to tablecount-1 do
  643.       recordsets[tables[ii].RecordSetIndex].matchrecord:=tables[ii].CurrentRecord;
  644.     result:=query.parser.Evaluate;
  645.   end;
  646.  
  647.  
  648.   function matchhaving(arecord:integer):boolean;
  649.   begin
  650.     recordsets[t3].matchrecord:=arecord;
  651.     result:=query.Parser.evaluate;
  652.   end;
  653.  
  654.   function expandall:string;
  655.   begin
  656.     result:=recordsets[t1].LongFieldList+';'+recordsets[t2].LongFieldList;
  657.   end;
  658.  
  659.   function settables:boolean; // JV 25-03-2002
  660.   // added alias option JV 27-03-2002
  661.   var
  662.     ii,tii,rrc,pp:integer;
  663.     atom, atomalias:string;
  664.   begin
  665.     result:=false;
  666.     setlength(tables,tablecount);
  667.     for ii:=0 to tablecount-1 do begin
  668.       atom:=gen[ii];
  669.       pp:=pos('|',atom);
  670.       if pp=0 then
  671.         atomalias:=atom
  672.       else begin
  673.         atomalias:=copy(atom,pp+1,maxint);
  674.         atom:=copy(atom,1,pp-1);
  675.       end;
  676.       err('SELECT: can not find table '+atom);
  677.       tii:=indexoftable(atom);
  678.       if tii=-1 then exit;
  679.       rrc:=recordsets[tii].recordcount;
  680.       err('SELECT: table '+atom+' has no records');
  681.       if rrc=0 then exit;
  682.       recordsets[tii].alias:=atomalias;
  683.       tables[ii].TableName:=atom;
  684.       tables[ii].TableAlias:=atomalias;
  685.       NameSpace.AddString(atom,tii,0);
  686.       NameSpace.AddString(atomalias,tii,0);
  687.       tables[ii].RecordSetIndex:=tii;
  688.       tables[ii].CurrentRecord:=0;
  689.       tables[ii].RecordCount:=rrc;
  690.     end;
  691.     result:=true;
  692.   end;
  693.  
  694.   procedure matchtables(t:integer);
  695.   var
  696.     ii,kk:integer;
  697.     debs:string;
  698.   begin
  699.     if t=tablecount-1 then begin
  700.       for ii:=0 to tables[t].RecordCount-1 do begin
  701.         tables[t].CurrentRecord:=ii;
  702.         if matchrecordsEx then begin
  703.            addresultoutputEx;
  704.         end;
  705.       end;
  706.     end
  707.     else begin
  708.       for ii:=0 to tables[t].RecordCount-1 do begin
  709.         tables[t].CurrentRecord:=ii;
  710.         matchtables(t+1);
  711.       end;
  712.     end;
  713.   end;
  714. begin
  715.   Fmatchinghaving:=false;
  716.   result:=0;
  717.   split(tablelist,gen);
  718.   err('SELECT: join table missing');
  719.   tablecount:=gen.count;
  720.   if tablecount<2 then exit;
  721.   if not settables then exit;
  722.  
  723.  
  724.   err('SELECT: missing field list');
  725.   if selectfields='' then exit;
  726.   if selectfields='*' then
  727.     selectfields:=expandall;
  728.  
  729. {new code}
  730.   err('SELECT dev: can not set output fields');
  731.   if not setoutputfields then begin
  732.     sqloutput.free;
  733.     exit;
  734.   end;
  735.  
  736.   // join fields are present, now join
  737.   if resultname<>'' then begin
  738.     // check if this is a persistent table
  739.     err('SELECT INTO: table '+resultname+' allready exists.');
  740.     if InCatalog(resultname) then begin
  741.       sqloutput.free;
  742.       exit;
  743.     end;
  744.     // check index
  745.     idx:=Frecordsets.IndexOf(resultname);
  746.     if idx=-1 then begin
  747.       t3:=AddRecordSet(resultname);
  748.       Recordsets[t3].intermediate:=true;
  749.     end
  750.     else begin
  751.       // check if this is a intermediate one
  752.       if recordsets[idx+1].intermediate then begin
  753.         FRecordsets.delete(idx);
  754.         t3:=AddRecordSet(resultname);
  755.         Recordsets[t3].intermediate:=true;
  756.       end
  757.       else begin
  758.         err('ASSIGN: table '+resultname+' is not a variable');
  759.         sqloutput.free;
  760.         exit;
  761.       end;
  762.     end;
  763.   end
  764.   else
  765.     t3:=AddRecordSet(uniquename);
  766.   result:=t3;
  767.  
  768.   // assign selectfields
  769.   split(sqloutput.FieldNames, recordsets[t3].FieldNames);
  770.  
  771.  
  772.   // copy field funcs
  773.   c:=recordsets[t3].FieldNames.Count;
  774.   setlength(recordsets[t3].FFieldFuncs,c);
  775.   for i:=0 to c-1 do
  776.     recordsets[t3].FFieldFuncs[i]:=selectfieldfunctions[i];
  777.   if wfrom<>0 then begin
  778.     query.Parser.GetTokenList(query.Ftokens,wfrom,wtill);
  779.   end;
  780.  
  781.    matchtables(0);
  782.  
  783.   // process any group by clause
  784.   if bAggregate and (recordsets[t3].recordcount>1) then
  785.     groupby(recordsets[t3],grouplist);
  786.  
  787.   FMatchrecordSet:=t3;
  788.   Fmatchinghaving:=true;
  789.   c3:=recordsets[t3].recordcount;
  790.   // process any having clause
  791.   if (having<>'') and (c3<>0) then begin
  792.     query.Parser.Expression:=having;
  793.     for i3:=0 to c3-1 do
  794.       recordsets[t3].records[i3].mark:=false;
  795.     for i3:=0 to c3-1 do
  796.       if not matchhaving(i3) then
  797.         recordsets[t3].records[i3].mark:=true;
  798.     for i3:=c3-1 downto 0 do
  799.       if recordsets[t3].records[i3].mark then
  800.         recordsets[t3].DeleteRecord(i3);
  801.   end;
  802.   // process any order by clause
  803.   if (orderbylist<>'') and (recordsets[t3].recordcount>1) then
  804.     sortRecordset(t3,0,recordsets[t3].recordcount,orderbylist,ascending);
  805.   sqloutput.free;  // JV 25-03-2002
  806. end;
  807.  
  808. function TjanSQL.openCatalog(value: string): integer;
  809. begin
  810.   result:=0;
  811.   err('Catalog '+value+' does not exist');
  812.   if not directoryexists(value) then exit;
  813.   FCatalog:=value;
  814.   result:=-1;
  815. end;
  816.  
  817. function TjanSQL.openTable(value: string;persistent:boolean): boolean;
  818. var
  819.   fn:string;
  820.   rs:TjanRecordSet;
  821. begin
  822.   result:=false;
  823.   if persistent then
  824.     if not directoryexists(FCatalog) then exit;
  825.   if FRecordSets.IndexOf(value)<>-1 then exit;
  826.   fn:=Fcatalog+'\'+value+'.txt';
  827.   if persistent then
  828.     if not fileexists(fn) then exit;
  829.   rs:=TjanRecordSet.create;
  830.   rs.name:=value;
  831.   rs.persistent:=persistent;
  832.   FRecordSets.AddObject(value,rs);
  833.   if persistent then
  834.     result:=rs.LoadFromFile(fn);
  835. end;
  836.  
  837.  
  838. function TjanSQL.uniqueName: string;
  839. begin
  840.   result:='$$$'+inttostr(FNameCounter);
  841.   inc(FNameCounter);
  842. end;
  843.  
  844. function TjanSQL.SelectFrom(query:TjanSQLQuery;tablename1, selectfields,
  845.   wherecondition,orderbylist: string;ascending:boolean;wfrom,wtill:integer;grouplist,having,resultname:string): integer;
  846. var
  847.   t1,t2,t3:integer;
  848.   i,c,i1,c1,i2,c2,i3,c3,ij,cj:integer;
  849.   idx:integer;
  850.   fieldname,fieldvalue:string;
  851.   outputfieldcount:integer;
  852.   selectfieldindexes:array of integer;
  853.   displayfield,displayfields:string;
  854.   selectfieldfunctions:array of TTokenOperator;
  855.   m2:integer;
  856.   bAggregate:boolean;
  857.   sqloutput:TjanSQLOutput;
  858.  
  859.  
  860.   function setgroupfunc(avalue:string;ii:integer):string;
  861.   var
  862.     ppo,ppc:integer;
  863.     sfun:string;
  864.   begin
  865.     result:=avalue;
  866.     selectfieldfunctions[ii]:=toNone;
  867.     ppo:=posstr('(',avalue);
  868.     if ppo>0 then begin
  869.       ppc:=posstr(')',avalue,ppo);
  870.       if ppc=0 then exit;
  871.       sfun:=lowercase(trim(copy(avalue,1,ppo-1)));
  872.       result:=copy(avalue,ppo+1,ppc-ppo-1);
  873.       if sfun='count' then begin
  874.         selectfieldfunctions[ii]:=tosqlCount;
  875.         bAggregate:=true;
  876.       end
  877.       else if sfun='sum' then begin
  878.         selectfieldfunctions[ii]:=tosqlSum;
  879.         bAggregate:=true;
  880.       end
  881.       else if sfun='avg' then begin
  882.         selectfieldfunctions[ii]:=tosqlAvg;
  883.         bAggregate:=true;
  884.       end
  885.       else if sfun='max' then begin
  886.         selectfieldfunctions[ii]:=tosqlMax;
  887.         bAggregate:=true;
  888.       end
  889.       else if sfun='min' then begin
  890.         selectfieldfunctions[ii]:=tosqlMin;
  891.         bAggregate:=true;
  892.       end
  893.       else if sfun='stddev' then begin
  894.         selectfieldfunctions[ii]:=tosqlStdDev;
  895.         bAggregate:=true;
  896.       end
  897.         else
  898.           result:=avalue;
  899.     end;
  900.   end;
  901.  
  902.  
  903.  
  904.   function setoutputfields:boolean;
  905.   var
  906.     ii,cc:integer;
  907.     ofld:TjanSQLCalcField;
  908.     ppa:integer;
  909.     sfield,salias,prefield:string;
  910.   begin
  911.     result:=false;
  912.     split(selectfields,gen);
  913.     cc:=gen.count;
  914.     outputfieldcount:=cc;
  915.     setlength(selectfieldfunctions,cc);
  916.     sqloutput:=TjanSQLOutput.create;
  917.     for ii:=0 to cc-1 do begin
  918.       ofld:=sqloutput.AddField;
  919.       ofld.Calculator.onGetVariable:=GetVariable;
  920.       sfield:=gen[ii];
  921.       ppa:=pos('|',sfield);
  922.       if ppa>0 then begin
  923.         prefield:=copy(sfield,1,ppa-1);
  924.         prefield:=setgroupfunc(prefield,ii);
  925.         ofld.name:=copy(sfield,ppa+1,maxint);
  926.         try
  927.           ofld.expression:=prefield;
  928.         except
  929.           exit;
  930.         end;
  931.       end
  932.       else begin
  933.         ofld.name:=setgroupfunc(sfield,ii);
  934.         try
  935.           ofld.expression:=sfield;
  936.         except
  937.           exit;
  938.         end;
  939.       end;
  940.     end;
  941.     result:=true;
  942.   end;
  943.  
  944.  
  945.  
  946.   function matchwhere(arecord:integer):boolean;
  947.   begin
  948.     if wherecondition='' then
  949.       result:=true
  950.     else begin
  951.       recordsets[t1].matchrecord:=arecord;
  952.       result:=query.Parser.evaluate;
  953.     end;
  954.   end;
  955.  
  956.   function matchhaving(arecord:integer):boolean;
  957.   begin
  958.     recordsets[t3].matchrecord:=arecord;
  959.     result:=query.Parser.evaluate;
  960.   end;
  961.  
  962.   procedure addresultoutput(arecord:integer);
  963.   var
  964.     ii,cc,ir:integer;
  965.     ss:string;
  966.     newr:TjanRecord;
  967.     v:variant;
  968.   begin
  969.     ir:=recordsets[t3].AddRecord;
  970.     cc:=sqloutput.FieldCount;
  971.     FMatchrecordSet:=t1;
  972.     recordsets[t1].matchrecord:=arecord;
  973.     for ii:=0 to cc-1 do begin
  974.        v:=sqloutput.Fields[ii].value;
  975.        ss:=v;
  976.        recordsets[t3].records[ir].fields[ii].value:=ss;
  977.     end;
  978.   end;
  979.  
  980.  
  981.   function expandall:string;
  982.   // expand * fields to all fieldname
  983.   begin
  984.     result:=recordsets[t1].ShortFieldList;
  985.   end;
  986.  
  987. begin
  988.   Fmatchinghaving:=false;
  989.   bAggregate:=false;
  990.   result:=0;
  991.   t1:=IndexOfTable(tablename1);
  992.   err('SELECT: can not find table '+tablename1);
  993.   if t1=-1 then exit;
  994.   err('SELECT: no selected fields');
  995.   if selectfields='' then exit;
  996.   if selectfields='*' then
  997.     selectfields:=expandall;
  998.   err('SELECT: can not find selected fields');
  999.  
  1000. {new code}
  1001.   err('SELECT dev: can not set output fields');
  1002.   if not setoutputfields then begin
  1003.     sqloutput.free;
  1004.     exit;
  1005.   end;
  1006.  
  1007.   if resultname<>'' then begin
  1008.     // check if this is a persistent table
  1009.     err('SELECT INTO: table '+resultname+' allready exists.');
  1010.     if InCatalog(resultname) then begin
  1011.       sqloutput.free;
  1012.       exit;
  1013.     end;
  1014.     // check index
  1015.     idx:=Frecordsets.IndexOf(resultname);
  1016.     if idx=-1 then begin
  1017.       t3:=AddRecordSet(resultname);
  1018.       Recordsets[t3].intermediate:=true;
  1019.     end
  1020.     else begin
  1021.       // check if this is a intermediate one
  1022.       if recordsets[idx+1].intermediate then begin
  1023.         FRecordsets.delete(idx);
  1024.         t3:=AddRecordSet(resultname);
  1025.         Recordsets[t3].intermediate:=true;
  1026.       end
  1027.       else begin
  1028.         err('ASSIGN: table '+resultname+' is not a variable');
  1029.         sqloutput.free;
  1030.         exit;
  1031.       end;
  1032.     end;
  1033.   end
  1034.   else
  1035.     t3:=AddRecordSet(uniquename);
  1036.   // assign selectfields
  1037.   // if * then expand
  1038.  
  1039.   split(sqloutput.FieldNames, recordsets[t3].FieldNames);
  1040.  
  1041.   c1:=recordsets[t1].recordcount;
  1042.   err('SELECT FROM: no records');
  1043.   if (c1=0) then  begin
  1044.     sqloutput.free;
  1045.     exit;
  1046.   end;
  1047.   // copy field funcs
  1048.   c:=recordsets[t3].FieldNames.Count;
  1049.   setlength(recordsets[t3].FFieldFuncs,c);
  1050.  
  1051.   for i:=0 to c-1 do
  1052.     recordsets[t3].FFieldFuncs[i]:=selectfieldfunctions[i];
  1053.  
  1054.   FMatchrecordSet:=t1;
  1055.   if wfrom<>0 then begin
  1056.     query.Parser.GetTokenList(query.Ftokens,wfrom,wtill);
  1057.   end;
  1058.   for i1:=0 to c1-1 do
  1059.     if matchwhere(i1) then begin
  1060.        addresultoutput(i1);
  1061.     end;
  1062.  
  1063.   FMatchrecordSet:=t3;
  1064.   // process any group by clause
  1065.   if bAggregate and (recordsets[t3].recordcount>1) then begin
  1066.     groupby(recordsets[t3],grouplist);
  1067.   end;
  1068.   c3:=recordsets[t3].recordcount;
  1069.   Fmatchinghaving:=true;
  1070.   // process any having clause
  1071.   if (having<>'') and (c3<>0) then begin
  1072.     query.Parser.Expression:=having;
  1073.     for i3:=0 to c3-1 do
  1074.       recordsets[t3].records[i3].mark:=false;
  1075.     for i3:=0 to c3-1 do
  1076.       if not matchhaving(i3) then
  1077.         recordsets[t3].records[i3].mark:=true;
  1078.     for i3:=c3-1 downto 0 do
  1079.       if recordsets[t3].records[i3].mark then
  1080.         recordsets[t3].DeleteRecord(i3);
  1081.   end;
  1082.  
  1083.   // process any order by clause
  1084.   if (orderbylist<>'') and (recordsets[t3].recordcount>1) then
  1085.     sortrecordset(t3,0,recordsets[t3].recordcount,orderbylist,ascending);
  1086.   sqloutput.free;
  1087.   result:=t3;
  1088. end;
  1089.  
  1090. function TjanSQL.IndexOfTable(tablename: string): integer;
  1091. begin
  1092.   result:=Frecordsets.IndexOf(tablename);
  1093.   if result=-1 then begin
  1094.      //  auto open tables used in queries
  1095.      if not OpenTable(tablename,true) then exit;
  1096.      result:=Frecordsets.IndexOf(tablename);
  1097.   end;
  1098.   inc(result);
  1099. end;
  1100.  
  1101. function TjanSQL.CreateTable(tablename, fields: string): integer;
  1102. var
  1103.   fn,s:string;
  1104. begin
  1105.   result:=0;
  1106.   if tablename='' then exit;
  1107.   if fields='' then exit;
  1108.   fn:=FCatalog+'\'+tablename+'.txt';
  1109.   if fileexists(fn) then exit;
  1110.   s:=fields;
  1111.   janSQLstrings.savestring(fn,s);
  1112.   result:=-1;
  1113. end;
  1114.  
  1115. function TjanSQL.DropTable(tablename: string): integer;
  1116. var
  1117.   fn:string;
  1118.   idx:integer;
  1119. begin
  1120.   result:=0;
  1121.   err('DROP TABLE: table name missing');
  1122.   if tablename='' then exit;
  1123.   fn:=FCatalog+'\'+tablename+'.txt';
  1124.   err('DROP TABLE: can not find file '+fn);
  1125.   if not fileexists(fn) then exit;
  1126.   deletefile(fn);
  1127.   idx:=FRecordSets.IndexOf(tablename);
  1128.   if idx<>-1 then
  1129.     FrecordSets.Delete(idx);
  1130.   result:=-1;
  1131. end;
  1132.  
  1133. function TjanSQL.DeleteFrom(tablename1, wherecondition: string): integer;
  1134. var
  1135.   c1,i1,t1:integer;
  1136.  
  1137.  
  1138.   function matchwhere(arecord:integer):boolean;
  1139.   begin
  1140.     FMatchrecordSet:=t1;
  1141.     recordsets[t1].matchrecord:=arecord;
  1142.     FEParser.Expression:=wherecondition;
  1143.     result:=FEParser.evaluate;
  1144.   end;
  1145.  
  1146.   procedure deletematch(arecord:integer);
  1147.   begin
  1148.     recordsets[t1].DeleteRecord(arecord);
  1149.   end;
  1150.  
  1151. begin
  1152.   result:=0;
  1153.   t1:=IndexOfTable(tablename1);
  1154.   if t1=-1 then exit;
  1155.   // check filter
  1156.   if wherecondition='' then exit;
  1157.   c1:=recordsets[t1].recordcount;
  1158.   if (c1=0) then exit;
  1159.   for i1:=c1-1 downto 0 do
  1160.     if matchwhere(i1) then
  1161.       DeleteMatch(i1);
  1162.   recordsets[t1].modified:=true;
  1163.   result:=-1;
  1164. end;
  1165.  
  1166. function TjanSQL.InsertInto(tablename1, columns, values: string): integer;
  1167. var
  1168.   i1,c1,t1,r1:integer;
  1169.   insertfields:TjanSQLFields;
  1170.  
  1171.   function parsevalues:boolean;
  1172.   var
  1173.     sline,stoken:string;
  1174.     LL,pp:integer;
  1175.   begin
  1176.     result:=false;
  1177.     LL:=0;
  1178.     sline:=trim(values);
  1179.     err('INSERT INTO parsing values:empty');
  1180.     if sline='' then exit;
  1181.     repeat
  1182.       if sline[1]='''' then begin
  1183.         pp:=posstr('''',sline,2);
  1184.         err('INSERT INTO parsing values: missing '' delimiter');
  1185.         if pp=0 then exit;
  1186.         inc(LL);
  1187.         setlength(insertfields,LL);
  1188.         insertfields[LL-1].FieldValue:=copy(sline,2,pp-2);
  1189.         sline:=trim(copy(sline,pp+1,maxint));
  1190.         if sline='' then // ready
  1191.           pp:=0
  1192.         else begin  // must have comma
  1193.           err('INSERT INTO parsing values:missing ,');
  1194.           if sline[1]<>',' then exit;
  1195.           sline:=trim(copy(sline,2,maxint));
  1196.         end;
  1197.       end
  1198.       else begin
  1199.         pp:=posstr(',',sline);
  1200.         if pp=0 then begin // single value
  1201.           inc(LL);
  1202.           setlength(insertfields,LL);
  1203.           insertfields[LL-1].FieldValue:=trim(sline);
  1204.         end
  1205.         else begin
  1206.           inc(LL);
  1207.           setlength(insertfields,LL);
  1208.           insertfields[LL-1].FieldValue:=trim(copy(sline,1,pp-1));
  1209.           sline:=trim(copy(sline,pp,maxint));
  1210.           if sline='' then // ready
  1211.             pp:=0
  1212.           else begin  // must have comma
  1213.             err('Missing , in '+sline);
  1214.             if sline[1]<>',' then exit;
  1215.             sline:=trim(copy(sline,2,maxint));
  1216.           end;
  1217.         end;
  1218.       end;
  1219.     until pp=0;
  1220.     result:=true;
  1221.   end;
  1222.  
  1223.   function parsecolumns:boolean;
  1224.   var
  1225.     ii,LL,pp,fii:integer;
  1226.     sline,stoken:string;
  1227.   begin
  1228.     result:=false;
  1229.     LL:=length(insertfields);
  1230.     if columns='' then begin
  1231.       for ii:=0 to LL-1 do
  1232.         insertfields[ii].FieldIndex:=ii;
  1233.     end
  1234.     else begin
  1235.       sline:=columns;
  1236.       err('INSERT INTO: number of columns and values is different');
  1237.       split(sline,gen);
  1238.       if gen.Count<>LL then exit; // number of columns and values not the same
  1239.       for ii:=0 to LL-1 do begin
  1240.         fii:=recordsets[t1].IndexOfField(gen[ii]);
  1241.         if fii=-1 then exit;
  1242.         insertfields[ii].FieldIndex:=fii;
  1243.       end;
  1244.     end;
  1245.     result:=true;
  1246.   end;
  1247.  
  1248.   procedure UpdateValues(arecord:integer);
  1249.   var
  1250.     ii,LL:integer;
  1251.   begin
  1252.     LL:=length(insertfields);
  1253.     if LL=0 then exit;
  1254.     for ii:=0 to LL-1 do begin
  1255.       recordsets[t1].records[arecord].fields[insertfields[ii].FieldIndex].value:=insertfields[ii].FieldValue;
  1256.     end;
  1257.   end;
  1258.  
  1259. begin
  1260.   result:=0;
  1261.   err('Missing table name in INSERT INTO component');
  1262.   if tablename1='' then exit;
  1263.   err('Missing values in VALUES component');
  1264.   if values='' then exit;
  1265.   t1:=IndexOfTable(tablename1);
  1266.   err('Table '+tablename1+' not open');
  1267.   if t1=-1 then exit;
  1268.   if not parsevalues then exit;
  1269.   if not parsecolumns then exit;
  1270.   r1:=recordsets[t1].AddRecord;
  1271.   updatevalues(r1);
  1272.   result:=-1;
  1273.   recordsets[t1].modified:=true;
  1274. end;
  1275.  
  1276. function TjanSQL.Update(query:TjanSQLQuery;tablename1, updatelist,
  1277.   wherecondition: string): integer;
  1278. var
  1279.   i1,c1,t1:integer;
  1280.   updatefields:TjanSQLFields;
  1281.   outputfieldcount:integer;
  1282.   sqloutput:TjanSQLOutput;
  1283.  
  1284.   function matchwhere(arecord:integer):boolean;
  1285.   begin
  1286.     FMatchrecordSet:=t1;
  1287.     recordsets[t1].matchrecord:=arecord;
  1288.     if wherecondition<>'' then begin
  1289.       result:=query.Parser.evaluate;
  1290.     end
  1291.     else
  1292.       result:=true;
  1293.   end;
  1294.  
  1295.  
  1296.  
  1297.   function parseUpdateList:boolean;
  1298.   // format userid=10, username='Jan verhoeven' etc
  1299.   var
  1300.     LL,pp,fii:integer;
  1301.     sline,stoken:string;
  1302.   begin
  1303.     result:=false;
  1304.     sline:=trim(updatelist);
  1305.     LL:=0;
  1306.     repeat
  1307.       pp:=posstr('=',sline);
  1308.       if pp=0 then exit;
  1309.       stoken:=trim(copy(sline,1,pp-1));
  1310.       sline:=trim(copy(sline,pp+1,maxint));
  1311.       if sline='' then exit;
  1312.       inc(LL);
  1313.       setlength(updatefields,LL);
  1314.       updatefields[LL-1].FieldName:=stoken;
  1315.       err('UPDATE: can not find field '+stoken);
  1316.       fii:=recordsets[t1].FieldNames.IndexOf(stoken);
  1317.       if fii=-1 then exit;
  1318.       updatefields[LL-1].FieldIndex:=fii;
  1319.       if sline[1]='''' then begin // text value
  1320.         pp:=posstr('''',sline,2);
  1321.         if pp=0 then exit;
  1322.         stoken:=copy(sline,2,pp-2);
  1323.         updatefields[LL-1].FieldValue:=stoken;
  1324.         sline:=trim(copy(sline,pp+1,maxint));
  1325.         if sline='' then
  1326.           pp:=0
  1327.         else begin
  1328.           if sline[1]<>',' then exit;
  1329.           system.Delete(sline,1,1);
  1330.         end;
  1331.       end
  1332.       else begin // not text value
  1333.         pp:=posstr(',',sline);
  1334.         if pp=0 then
  1335.           updatefields[LL-1].FieldValue:=trim(sline)
  1336.         else begin
  1337.           stoken:=trim(copy(sline,1,pp-1));
  1338.           updatefields[LL-1].FieldValue:=stoken;
  1339.           sline:=copy(sline,pp+1,maxint);
  1340.         end;
  1341.       end;
  1342.     until pp=0;
  1343.     result:=true;
  1344.   end;
  1345.  
  1346.   procedure UpdateMatch(arecord:integer);
  1347.   var
  1348.     ii,LL:integer;
  1349.   begin
  1350.     LL:=length(updatefields);
  1351.     if LL=0 then exit;
  1352.     for ii:=0 to LL-1 do begin
  1353.       recordsets[t1].records[arecord].fields[updatefields[ii].FieldIndex].value:=updatefields[ii].FieldValue;
  1354.     end;
  1355.   end;
  1356.  
  1357.   function setoutputfields:boolean;
  1358.   var
  1359.     ii,cc,fii:integer;
  1360.     ofld:TjanSQLCalcField;
  1361.     ppa:integer;
  1362.     sfield,salias,prefield:string;
  1363.   begin
  1364.     result:=false;
  1365.     split(updatelist,gen);
  1366.     cc:=gen.count;
  1367.     outputfieldcount:=cc;
  1368.     sqloutput:=TjanSQLOutput.create;
  1369.     for ii:=0 to cc-1 do begin
  1370.       ofld:=sqloutput.AddField;
  1371.       ofld.Calculator.onGetVariable:=GetVariable;
  1372.       sfield:=gen[ii];
  1373.       ppa:=pos('=',sfield);
  1374.       if ppa=0 then exit;
  1375.       prefield:=copy(sfield,ppa+1,maxint);
  1376.       ofld.name:=trim(copy(sfield,1,ppa-1));
  1377.       fii:=recordsets[t1].IndexOfField(ofld.name);
  1378.       if fii=-1 then exit;
  1379.       ofld.FieldIndex:=fii;
  1380.       try
  1381.         ofld.expression:=trim(prefield);
  1382.       except
  1383.         exit;
  1384.       end;
  1385.     end;
  1386.     result:=true;
  1387.   end;
  1388.  
  1389.   // JV 26-03-2002
  1390.   procedure updateresult(arecord:integer);
  1391.   var
  1392.     ii,cc,ir,fii:integer;
  1393.     ss:string;
  1394.     newr:TjanRecord;
  1395.     v:variant;
  1396.   begin
  1397.     cc:=sqloutput.FieldCount;
  1398.     FMatchrecordSet:=t1;
  1399.     recordsets[t1].matchrecord:=arecord;
  1400.     for ii:=0 to cc-1 do begin
  1401.        fii:=sqloutput.fields[ii].FieldIndex;
  1402.        v:=sqloutput.Fields[ii].value;
  1403.        ss:=v;
  1404.        recordsets[t1].records[arecord].fields[fii].value:=ss;
  1405.     end;
  1406.   end;
  1407.  
  1408.  
  1409. begin
  1410.   result:=0;
  1411.   err('UPDATE: missing tablename');
  1412.   if tablename1='' then exit;
  1413.   err('UPDATE: missing update fields');
  1414.   if updatelist='' then exit;
  1415.   t1:=IndexOfTable(tablename1);
  1416.   err('UPDATE: cannot find table '+tablename1);
  1417.   if t1=-1 then exit;
  1418.   // table is open;
  1419.   err('UPDATE: can not parse updatelist');
  1420.   // JV 26-Mar-2002
  1421.   err('SELECT dev: can not set output fields');
  1422.   if not setoutputfields then begin
  1423.     sqloutput.free;
  1424.     exit;
  1425.   end;
  1426.  
  1427.   c1:=recordsets[t1].recordcount;
  1428.   if (c1=0) then begin
  1429.     sqloutput.free;
  1430.     result:=-1;
  1431.     exit;
  1432.   end;
  1433.   FMatchrecordSet:=t1;
  1434.   if wherecondition<>'' then begin
  1435.     query.Parser.Expression:=wherecondition;
  1436.   end;
  1437.  
  1438.   for i1:=0 to c1-1 do
  1439.     if matchwhere(i1) then
  1440.       UpdateResult(i1); // Jv 26-Mar-2002
  1441.   sqloutput.free;
  1442.   result:=-1;
  1443.   recordsets[t1].modified:=true;
  1444. end;
  1445.  
  1446. // save all modified persistent files to disk
  1447. function TjanSQL.Commit(query:TjanSQLQuery): integer;
  1448. var
  1449.   i,c:integer;
  1450.   fn:string;
  1451. begin
  1452.   result:=0;
  1453.   c:=recordsetcount;
  1454.   if c=0 then begin
  1455.     result:=-1;
  1456.     exit;
  1457.   end;
  1458.   for i:=1 to c do
  1459.     if (recordsets[i].persistent) and (recordsets[i].modified) then begin
  1460.       fn:=Fcatalog+'\'+recordsets[i].name+'.txt';
  1461.       if not fileexists(fn) then exit;
  1462.       recordsets[i].SaveToFile(fn);
  1463.       recordsets[i].modified:=false;
  1464.     end;
  1465.   result:=-1;
  1466. end;
  1467.  
  1468. function TjanSQL.getRecordSetCount: integer;
  1469. begin
  1470.   result:=Frecordsets.Count;
  1471. end;
  1472.  
  1473. function TjanSQL.AddTableColumn(tablename, column, value: string): integer;
  1474. var
  1475.   t1:integer;
  1476. begin
  1477.   result:=0;
  1478.   t1:=indexoftable(tablename);
  1479.   if t1=-1 then exit;
  1480.   result:=recordsets[t1].AddField(column,value);
  1481.   if result<>0 then
  1482.     recordsets[t1].modified:=true;
  1483. end;
  1484.  
  1485. function TjanSQL.DropTableColumn(tablename, column: string): integer;
  1486. var
  1487.   t1:integer;
  1488. begin
  1489.   result:=0;
  1490.   t1:=indexoftable(tablename);
  1491.   if t1=0 then exit;
  1492.   result:=recordsets[t1].DeleteField(column);
  1493.   if result<>0 then
  1494.     recordsets[t1].modified:=true;
  1495. end;
  1496.  
  1497. // the main function that executes one or more sql statements seperated by a ;
  1498. function TjanSQL.SQLDirect(value: string): integer;
  1499. var
  1500.   sline,stoken:string;
  1501.   p:integer;
  1502.   i,c:integer;
  1503.   resultset:integer;
  1504. begin
  1505.   result:=0;
  1506.   FError:='';
  1507.   err('Empty SQL statement');
  1508.   if value='' then exit;
  1509.   value:=trim(value);
  1510.   split(value,FSQL);
  1511.   c:=FSQL.count;
  1512.   if c=0 then exit;
  1513.   // remove any empty lines
  1514.   for i:=c-1 downto 0 do
  1515.     if trim(FSQL[i])='' then
  1516.       FSQL.Delete(i);
  1517.   c:=FSQL.count;
  1518.   if c=0 then exit;
  1519.   for i:=0 to c-1 do begin
  1520.     namespace.Clear;
  1521.     resultset:=ISQL(FSQL[i]);
  1522.     if resultset=0 then exit;
  1523.   end;
  1524.   result:=resultset;
  1525. end;
  1526.  
  1527.  
  1528. function TjanSQL.SQLDirectStatement(query:TjanSQLQuery;value: string): integer;
  1529. var
  1530.   sline,stoken:string;
  1531.   p:integer;
  1532.   tokenizer:TjanSQLTokenizer;
  1533.   b:boolean;
  1534. begin
  1535.   query.ClearTokenList;
  1536.   result:=0;
  1537.   sline:=trim(stringreplace(value,cr,' ',[rfreplaceall]));
  1538.   err('Empty SQL statement');
  1539.   if sline='' then exit;
  1540.   err('Could not tokenize: '+sline);
  1541.   tokenizer:=TjanSQLTokenizer.create;
  1542.   tokenizer.onSubExpression:=procSubExpression;
  1543.   try
  1544.     b:=Tokenizer.Tokenize(sline,query.Ftokens)
  1545.   finally
  1546.     tokenizer.free;
  1547.   end;
  1548.   if not b then exit;
  1549.   err('No tokens');
  1550.   if query.Ftokens.Count=0 then exit;
  1551.   case query.tokens[0].operator of
  1552.   tosqlSELECT: result:=SQLSelect(query,sline,'');
  1553.   tosqlASSIGN: result:=SQLAssign(query,sline);
  1554.   tosqlSAVETABLE: result:=SQLSaveTable(query,sline);
  1555.   tosqlRELEASETABLE: result:=SQLReleaseTable(query,sline);
  1556.   tosqlINSERT:
  1557.     begin
  1558.       if query.Ftokens.Count<5 then exit;
  1559.       if query.Tokens[3].operator=tosqlselect then
  1560.         result:=SQLInsertSelect(query,sline)
  1561.       else
  1562.         result:=SQLInsert(query,sline);
  1563.     end;
  1564.   tosqlDELETE: result:=SQLDelete(query,sline);
  1565.   tosqlUPDATE: result:=SQLUpdate(query,sline);
  1566.   tosqlCREATE: result:=SQLCreate(query,sline);
  1567.   tosqlDROP:   result:=SQLDrop(query,sline);
  1568.   tosqlALTER:  result:=SQLAlter(query,sline);
  1569.   tosqlCOMMIT: result:=SQLCommit(query,sline);
  1570.   tosqlCONNECT:result:=SQLConnect(query,sline);
  1571.   else
  1572.     err('Unknown SQL command');
  1573.   end;
  1574. end;
  1575.  
  1576.  
  1577. // ALTER TABLE ADD COLUMN columnname
  1578. // ALTER TABLE DROP COLUMN columnname
  1579. function TjanSQL.SQLAlter(query:TjanSQLQuery;aline: string): integer;
  1580. var
  1581.   tablename,column:string;
  1582.   p,t1,L:integer;
  1583. begin
  1584.   result:=0;
  1585.   L:=query.FTokens.count;
  1586.   if L<6 then exit;
  1587.   p:=0;
  1588.   // check
  1589.   if (query.tokens[0].operator<>tosqlAlter) or
  1590.     (query.tokens[1].operator<>tosqlTABLE) or
  1591.     (query.tokens[4].operator<>tosqlCOLUMN)
  1592.      then exit;
  1593.   // tablename
  1594.   tablename:=query.tokens[2].name;
  1595.   t1:=indexoftable(tablename);
  1596.   if t1=-1 then exit;
  1597.   // add or drop
  1598.   if query.tokens[3].operator=tosqlADD then begin
  1599.     column:=query.tokens[5].name;
  1600.     if L>6 then
  1601.       result:=AddTableColumn(tablename,column,query.tokens[6].name)
  1602.     else
  1603.       result:=AddTableColumn(tablename,column,'')
  1604.   end
  1605.   else if query.tokens[3].operator=tosqlDROP then begin
  1606.     column:=query.tokens[5].name;
  1607.     result:=DropTableColumn(tablename,column);
  1608.   end
  1609.   else
  1610.     exit;
  1611. end;
  1612. // syntax: COMMIT
  1613. function TjanSQL.SQLCommit(query:TjanSQLQuery;aline: string): integer;
  1614. var
  1615.   tablename,fieldlist,column:string;
  1616.   p,t1,L:integer;
  1617. begin
  1618.   result:=-1;
  1619.   L:=query.FTokens.count;
  1620.   if L<>1 then exit;
  1621.   if query.Tokens[0].operator<>tosqlCOMMIT then exit;
  1622.   result:=Commit(query);
  1623. end;
  1624.  
  1625. // syntax: CREATE TABLE tablename (column1[,columnN])
  1626. function TjanSQL.SQLCreate(query:TjanSQLQuery;aline: string): integer;
  1627. var
  1628.   tablename,fieldlist,column:string;
  1629.   p,t1,L:integer;
  1630. begin
  1631.   result:=0;
  1632.   L:=query.FTokens.count;
  1633.   if L<6 then exit;
  1634.   if (query.Tokens[0].operator<>tosqlCREATE) or
  1635.      (query.Tokens[1].operator<>tosqlTABLE)
  1636.      then exit;
  1637.   tablename:=query.tokens[2].name;
  1638.   if query.tokens[3].tokenkind<>tkOpen then exit;
  1639.   if query.tokens[L-1].tokenkind<>tkClose then exit;
  1640.   fieldlist:='';
  1641.   for p:=4 to L-2 do
  1642.     if query.tokens[p].name=',' then
  1643.       fieldlist:=fieldlist+';'
  1644.     else
  1645.       fieldlist:=fieldlist+query.tokens[p].name;
  1646.   result:=CreateTable(tablename,fieldlist);
  1647. end;
  1648.  
  1649. // syntax: DELETE FROM tablename WERE condition
  1650. function TjanSQL.SQLDelete(query:TjanSQLQuery;aline: string): integer;
  1651. var
  1652.   tablename,condition:string;
  1653.   p,t1,L:integer;
  1654. begin
  1655.   result:=0;
  1656.   L:=query.FTokens.count;
  1657.   if L<7 then exit;
  1658.   if (query.Tokens[0].operator<>tosqlDELETE) or
  1659.      (query.Tokens[1].operator<>tosqlFROM)
  1660.      then exit;
  1661.   tablename:=query.tokens[2].name;
  1662.   if query.tokens[3].operator<>tosqlWHERE then exit;
  1663.   for p:=4 to L-1 do
  1664.     condition:=condition+query.tokens[p].name;
  1665.   result:=deletefrom(tablename,condition);
  1666. end;
  1667.  
  1668. // syntax DROP TABLE tablename
  1669. function TjanSQL.SQLDrop(query:TjanSQLQuery;aline: string): integer;
  1670. var
  1671.   tablename,fieldlist,column:string;
  1672.   p,t1,L:integer;
  1673. begin
  1674.   result:=0;
  1675.   L:=query.FTokens.count;
  1676.   if L<3 then exit;
  1677.   if (query.Tokens[0].operator<>tosqlDROP) or
  1678.      (query.Tokens[1].operator<>tosqlTABLE)
  1679.      then exit;
  1680.   tablename:=query.tokens[2].name;
  1681.   result:=DropTable(tablename);
  1682. end;
  1683.  
  1684. // syntax INSERT INTO tablename VALUES (value1,[value2])
  1685. // or: INSERT INTO tablename (column1[,columnN) VALUES (value1,[valueN])
  1686. function TjanSQL.SQLInsert(query:TjanSQLQuery;aline: string): integer;
  1687. var
  1688.   tablename,fieldlist,column:string;
  1689.   columns,values:string;
  1690.   p1,p2,t1,L:integer;
  1691. begin
  1692.   result:=0;
  1693.   L:=query.FTokens.count;
  1694.   if L<4 then exit;
  1695.   if (query.Tokens[0].operator<>tosqlINSERT) or
  1696.      (query.Tokens[1].operator<>tosqlINTO)
  1697.      then exit;
  1698.   tablename:=query.tokens[2].name;
  1699.   p1:=3;
  1700.   columns:='';
  1701.   if query.Tokens[p1].tokenkind=tkOpen then begin  // have columns
  1702.     inc(p1);
  1703.     p2:=p1;
  1704.     while (p2<L) and (query.tokens[p2].tokenkind<>tkClose) do
  1705.       inc(p2);
  1706.     if p2>=L then exit; // missing )
  1707.     if (p1+1)=p2 then exit; // no columns
  1708.     for p1:=p1 to p2-1 do
  1709.       if query.Tokens[p1].name=',' then
  1710.         columns:=columns+';'
  1711.       else
  1712.         columns:=columns+query.Tokens[p1].name;
  1713.     p1:=p2+1;
  1714.   end;
  1715.   err('SQLInsert: missing VALUES');
  1716.   if p1+2>=L then exit;
  1717.   if query.Tokens[p1].operator=tosqlVALUES then begin
  1718.     inc(p1);
  1719.     err('SQLInsert: missing ( after VALUES');
  1720.     if query.Tokens[p1].tokenkind<>tkOpen then exit;
  1721.     inc(p1);
  1722.     p2:=p1;
  1723.     while (p2<L) and (query.tokens[p2].tokenkind<>tkClose) do
  1724.       inc(p2);
  1725.     err('SQLInsert: missing ) after VALUES');
  1726.     if p2>=L then exit; // missing )
  1727.     err('SQLInsert: no values after VALUES');
  1728.     if (p1+1)=p2 then exit; // no values
  1729.     for p1:=p1 to p2-1 do begin
  1730.       if query.tokens[p1].operator=toString then
  1731.         values:=values+''''+query.Tokens[p1].name+''''
  1732.       else
  1733.         values:=values+query.Tokens[p1].name;
  1734.     end;
  1735.   end
  1736.   else
  1737.     exit;
  1738.   result:=insertinto(tablename,columns,values);
  1739. end;
  1740.  
  1741. // syntax: SELECT fieldlist FROM tablename WHERE condition
  1742. // or: SELECT fieldlist FROM tablename
  1743. function TjanSQL.SQLSelect(query:TjanSQLQuery;aline,aname: string): integer;
  1744. var
  1745.   tablename,tablelist,fieldlist,condition,orderbylist:string;
  1746.   grouplist,having:string;
  1747.   ascending:boolean;
  1748.   tmp:string;
  1749.   p1,p2,t1,L:integer;
  1750.   wfrom,wtill:integer;
  1751.   bracketopen:integer;
  1752.   alias:integer;
  1753. begin
  1754.   result:=0;
  1755.   grouplist:='';
  1756.   having:='';
  1757.   orderbylist:='';
  1758.   ascending:=true;
  1759.   L:=query.FTokens.count;
  1760.   err('SELECT: Need at least 4 token');
  1761.   if L<4 then exit;
  1762.   err('SELECT: missing SELECT');
  1763.   if (query.Tokens[0].operator<>tosqlSELECT) then exit;
  1764.   for p2:=1 to L-1 do
  1765.     if query.Tokens[p2].operator=tosqlFROM then break;
  1766.   err('SELECT: missing FROM');
  1767.   if query.Tokens[p2].operator<>tosqlFROM then exit;
  1768.   // fieldlist
  1769.   err('SELECT: missing selected field list');
  1770.   if p2<2 then exit;
  1771.  
  1772. // catch for any comma's in functions
  1773.   bracketopen:=0;
  1774.   for p1:=1 to p2-1 do begin
  1775.     case query.tokens[p1].operator of
  1776.     toOpen:
  1777.       begin
  1778.         fieldlist:=fieldlist+'(';
  1779.         inc(bracketopen);
  1780.       end;
  1781.     toClose:
  1782.       begin
  1783.         fieldlist:=fieldlist+')';
  1784.         dec(bracketopen);
  1785.       end;
  1786.     toComma:
  1787.       begin
  1788.        if bracketopen=0 then
  1789.          fieldlist:=fieldlist+';'
  1790.        else
  1791.          fieldlist:=fieldlist+',';
  1792.       end;
  1793.     tosqlAS: fieldlist:=fieldlist+'|';
  1794.     else
  1795.       fieldlist:=fieldlist+query.Tokens[p1].name;
  1796.     end
  1797.   end;
  1798.   p1:=p2+1;
  1799.   if p1>=L then exit;
  1800.  
  1801.   // new
  1802.   alias:=0;
  1803.   while (p1<L) and (query.tokens[p1].tokenkind in [tkComma,tkOperand]) do begin
  1804.      if query.tokens[p1].tokenkind=tkComma then begin
  1805.        tablelist:=tablelist+';';
  1806.        alias:=0;
  1807.      end
  1808.      else begin
  1809.        inc(alias);
  1810.        if alias>=2 then
  1811.          tablelist:=tablelist+'|'+query.tokens[p1].name
  1812.        else
  1813.          tablelist:=tablelist+query.tokens[p1].name;
  1814.      end;
  1815.      inc(p1);
  1816.   end;
  1817.   // end new
  1818.   condition:='';
  1819.   wfrom:=0;
  1820.   if p1<L then begin
  1821.     if query.tokens[p1].operator=tosqlWHERE then begin
  1822.       inc(p1);
  1823.       err('SELECT: missing expression after WHERE');
  1824.       if (p1+1)>=L then exit;
  1825.       wfrom:=p1;
  1826.       while (p1<L) and (not(query.tokens[p1].operator in [tosqlORDER,tosqlGROUP])) do begin
  1827.         if query.tokens[p1].operator=tostring then
  1828.           condition:=condition+''''+query.tokens[p1].name+''' '
  1829.         else
  1830.           condition:=condition+query.tokens[p1].name+' ';
  1831.         wtill:=p1;
  1832.         inc(p1);
  1833.       end;
  1834.     end;
  1835.     // check for GROUP BY clause
  1836.     if (p1<(L-1)) and (query.tokens[p1].operator=tosqlGROUP) then begin
  1837.       inc(p1);
  1838.       while (p1<L) and (not(query.tokens[p1].operator in [tosqlORDER,tosqlHAVING])) do begin
  1839.         grouplist:=grouplist+query.tokens[p1].name;
  1840.         inc(p1);
  1841.       end;
  1842.       grouplist:=stringreplace(grouplist,',',';',[rfreplaceall]);
  1843.     end;
  1844.     // check for HAVING clause
  1845.     if (p1<(L-1)) and (query.tokens[p1].operator=tosqlHAVING) then begin
  1846.       inc(p1);
  1847.       while (p1<L) and (not(query.tokens[p1].operator in [tosqlORDER])) do begin
  1848.         having:=having+query.tokens[p1].name;
  1849.         inc(p1);
  1850.       end;
  1851.     end;
  1852.     // check for ORDER BY clause
  1853.     if (p1<(L-1)) and (query.tokens[p1].operator=tosqlORDER) then begin
  1854.       inc(p1);
  1855.       while (p1<L) do begin
  1856.         if query.tokens[p1].operator=tosqlASC then begin
  1857.           orderbylist:=orderbylist+'+';
  1858.         end
  1859.         else if query.tokens[p1].operator=tosqlDESC then
  1860.           orderbylist:=orderbylist+'-'
  1861.         else
  1862.           orderbylist:=orderbylist+query.tokens[p1].name;
  1863.         inc(p1);
  1864.       end;
  1865.       inc(p1);
  1866.       orderbylist:=stringreplace(orderbylist,',',';',[rfreplaceall]);
  1867.     end;
  1868.   end;
  1869.   if posstr(';',tablelist)>0 then
  1870.     result:=selectfromjoin(query,fieldlist,tablelist,condition,orderbylist,ascending,wfrom,wtill,grouplist,having,aname)
  1871.   else
  1872.     result:=selectfrom(query,tablelist,fieldlist,condition,orderbylist,ascending,wfrom,wtill,grouplist,having,aname);
  1873. end;
  1874.  
  1875. // syntax: UPDATE tablename SET field1=value1[,fieldN=valueN] WHERE condition
  1876. function TjanSQL.SQLUpdate(query:TjanSQLQuery;aline: string): integer;
  1877. var
  1878.   tablename,fieldlist,condition:string;
  1879.   columns,values:string;
  1880.   p1,p2,t1,L:integer;
  1881.   bCondition:boolean;
  1882.   brackets:integer;
  1883. begin
  1884.   result:=0;
  1885.   L:=query.FTokens.count;
  1886.   if L<6 then exit;
  1887.   if (query.Tokens[0].operator<>tosqlUPDATE) then exit;
  1888.   tablename:=query.tokens[1].name;
  1889.   if (query.Tokens[2].operator<>tosqlSET) then exit;
  1890.   columns:='';
  1891.   // parse to WHERE
  1892.   p1:=3;
  1893.   bCondition:=true;
  1894.   // parse fieldlist
  1895.   // allow for commas in multiparamer functions
  1896.   brackets:=0;
  1897.   while (p1<L) and (query.Tokens[p1].operator<>tosqlWHERE) do begin
  1898.     if query.tokens[p1].tokenkind=tkOpen then begin
  1899.       fieldlist:=fieldlist+'(';
  1900.       inc(brackets);
  1901.     end
  1902.     else if query.tokens[p1].tokenkind=tkClose then begin
  1903.       fieldlist:=fieldlist+')';
  1904.       dec(brackets);
  1905.     end
  1906.     else if query.tokens[p1].tokenkind=tkcomma then begin
  1907.       if brackets=0 then
  1908.         fieldlist:=fieldlist+';'
  1909.       else
  1910.         fieldlist:=fieldlist+','
  1911.     end
  1912.     else if query.tokens[p1].operator=toString then
  1913.       fieldlist:=fieldlist+''''+query.tokens[p1].name+''' '
  1914.     else
  1915.       fieldlist:=fieldlist+query.tokens[p1].name+' ';
  1916.     inc(p1);
  1917.   end;
  1918.   // parse condition
  1919.   condition:='';
  1920.   if (p1<L) and (query.Tokens[p1].operator=tosqlWHERE) then begin
  1921.     inc(p1);
  1922.     while (p1<L) do begin
  1923.       if query.tokens[p1].operator=toString then
  1924.         condition:=condition+''''+query.tokens[p1].name+''' '
  1925.       else
  1926.         condition:=condition+query.tokens[p1].name+' ';
  1927.       inc(p1);
  1928.     end;
  1929.   end;
  1930.   result:=update(query,tablename,fieldlist,condition);
  1931. end;
  1932.  
  1933.  
  1934. function TjanSQL.SQLConnect(query:TjanSQLQuery;aline: string): integer;
  1935. var
  1936.   catalog:string;
  1937.   L:integer;
  1938. begin
  1939.   result:=0;
  1940.   L:=query.FTokens.count;
  1941.   err('Expected 2 statement parts');
  1942.   if L<2 then exit;
  1943.   err('CONNECT TO expected');
  1944.   if (query.Tokens[0].operator<>tosqlCONNECT) then exit;
  1945.   CATALOG:=query.tokens[1].name;
  1946.   result:=opencatalog(catalog);
  1947. end;
  1948.  
  1949. function TjanSQL.Error: string;
  1950. begin
  1951.   result:=FError;
  1952. end;
  1953.  
  1954.  
  1955. procedure TjanSQL.getvariable(sender: Tobject;const VariableName: string;
  1956.   var VariableValue: variant; var handled: boolean);
  1957. var
  1958.   v:variant;
  1959.   index,p:integer;
  1960.   tablename,fieldname:string;
  1961.   arecordset,arecord:integer;
  1962.   anId,anExId:integer;
  1963. begin
  1964.   p:=0;
  1965.   if not Fmatchinghaving then
  1966.     p:=pos('.',VariableName);
  1967.   if p>0 then begin
  1968.     tablename:=copy(VariableName,1,p-1);
  1969.     if not namespace.Hash(tablename,aRecordset,anExId) then exit;
  1970.     arecord:=recordsets[arecordset].matchrecord;
  1971.     fieldname:=copy(VariableName,p+1,maxint);
  1972.   end
  1973.   else begin  // select without join
  1974.     arecordset:=Fmatchrecordset;
  1975.     arecord:=recordsets[arecordset].matchrecord;
  1976.     fieldname:=VariableName;
  1977.   end;
  1978.   index:=recordsets[arecordset].IndexOfField(fieldname);
  1979.   if index<>-1 then begin
  1980.     VariableValue:=recordsets[arecordset].records[arecord].fields[index].value;
  1981.     handled:=true;
  1982.   end;
  1983. end;
  1984.  
  1985.  
  1986.  
  1987. procedure TjanSQL.Sort(arecordset,From, Count: Integer;orderby:array of TjanSQLSort);
  1988.   procedure   Sort( iL, iR : Integer ) ;
  1989.   var
  1990.       L, R, M : Integer ;
  1991.   begin
  1992.       repeat
  1993.               L := iL ;
  1994.                   R := iR ;
  1995.                   M := ( L + R ) div 2 ;
  1996.  
  1997.                   repeat
  1998.                       while Compare(arecordset, From + L, From + M ,orderby) < 0 do Inc(L) ;
  1999.                       while Compare(arecordset, From + M, From + R ,orderby) < 0 do Dec(R) ;
  2000.                       if L <= R then begin
  2001.                               Swap(arecordset, From + L, From + R ) ;
  2002.                               if M = L then
  2003.                                   M := R
  2004.                               else if M = R then
  2005.                                   M := L ;
  2006.                               Inc(L) ;
  2007.                               Dec(R) ;
  2008.                       end ;
  2009.                   until L > R ;
  2010.  
  2011.                   if ( R - iL ) > ( iR - L ) then begin {Sort left here}
  2012.                       if L < iR then
  2013.                               Sort( L, iR ) ;
  2014.                       iR := R ;
  2015.                   end else begin
  2016.                       if iL < R then
  2017.                               Sort( iL, R ) ;
  2018.                       iL := L ;
  2019.                   end ;
  2020.           until iL >= iR ;
  2021.   end ;
  2022. begin
  2023.   if Count > 1 then
  2024.       Sort( 0, Count - 1 ) ;
  2025. end;
  2026.  
  2027. function TjanSQL.Compare(arecordset,i, j: Integer;orderby:array of TjanSQLSort): Integer;
  2028. var
  2029.   v:variant;
  2030.   s1,s2:string;
  2031.   index,p:integer;
  2032.   tablename,fieldname:string;
  2033.   arecord:integer;
  2034.   obi,obc:integer;
  2035.  
  2036.   function safefloat(atext:string):double;
  2037.   begin
  2038.     try
  2039.       result:=strtofloat(atext);
  2040.     except
  2041.       result:=0;
  2042.     end;
  2043.   end;
  2044.  
  2045.   function comparefloats(afloat1,afloat2:double):integer;
  2046.   begin
  2047.     if afloat1=afloat2 then
  2048.       result:=0
  2049.     else if afloat1>afloat2 then
  2050.       result:=1
  2051.     else
  2052.       result:=-1;
  2053.   end;
  2054. begin
  2055.   result:=0;
  2056.   arecord:=recordsets[arecordset].matchrecord;
  2057.   obc:=length(orderby);
  2058.   for obi:=0 to obc-1 do begin
  2059.     index:=orderby[obi].FieldIndex;
  2060.     s1:=recordsets[arecordset].records[i].fields[index].value;
  2061.     s2:=recordsets[arecordset].records[j].fields[index].value;
  2062.     if orderby[obi].SortAscending then begin
  2063.       if orderby[obi].SortNumeric then
  2064.         result:=comparefloats(safefloat(s1),safefloat(s2))
  2065.       else
  2066.         result:=ansicomparestr(s1,s2);
  2067.       if result<>0 then break;
  2068.     end
  2069.     else begin
  2070.       if orderby[obi].SortNumeric then
  2071.         result:=comparefloats(safefloat(s2),safefloat(s1))
  2072.       else
  2073.         result:=ansicomparestr(s2,s1);
  2074.       if result<>0 then break;
  2075.     end
  2076.   end;
  2077. end;
  2078.  
  2079. procedure TjanSQL.Swap(arecordset,i, j: Integer);
  2080. begin
  2081.   recordsets[arecordset].FRecords.Exchange(i,j);
  2082. end;
  2083.  
  2084. function TjanSQL.ReleaseRecordset(arecordset: integer): boolean;
  2085. var
  2086.   idx:integer;
  2087. begin
  2088.   result:=false;
  2089.   if arecordset<1 then exit;
  2090.   if arecordset>recordsetcount then exit;
  2091.   idx:=arecordset-1;  //
  2092.   FrecordSets.Delete(idx);
  2093.   result:=true;
  2094. end;
  2095.  
  2096. procedure TjanSQL.ClearQueries;
  2097. var
  2098.   i,c:Integer;
  2099. begin
  2100.   c:=FQueries.Count;
  2101.   if c=0 then exit;
  2102.   for i:=0 to c-1 do
  2103.     TjanSQLQuery(FQueries[i]).free;
  2104.   FQueries.clear;
  2105. end;
  2106.  
  2107. function TjanSQL.AddQuery: TjanSQLQuery;
  2108. begin
  2109.   result:=TjanSQLQuery.create;
  2110.   result.engine:=self;
  2111.   Fqueries.Add(result);
  2112. end;
  2113.  
  2114. function TjanSQL.DeleteQuery(query: TjanSQLQuery): boolean;
  2115. var
  2116.   idx:integer;
  2117. begin
  2118.   result:=false;
  2119.   idx:=FQueries.IndexOf(query);
  2120.   if idx<>-1 then begin
  2121.     TjanSQLQuery(FQueries[idx]).free;
  2122.     Fqueries.Delete(idx);
  2123.     result:=true;
  2124.   end;
  2125. end;
  2126.  
  2127. function TjanSQL.ISQL(value: string): integer;
  2128. var
  2129.   qry:TjanSQLQuery;
  2130. begin
  2131.   result:=0;
  2132.   qry:=AddQuery;
  2133.   qry.Parser.onGetVariable:=getvariable;
  2134.   try
  2135.     result:=SQLDirectStatement(qry,value);
  2136.   finally
  2137.     DeleteQuery(qry);
  2138.   end;
  2139. end;
  2140.  
  2141. procedure TjanSQL.procsubexpression(sender: Tobject;
  2142.   const subexpression: string; var subexpressionValue: variant;
  2143.   var handled: boolean);
  2144. var
  2145.   sqlresult:integer;
  2146.  
  2147.   function getresultlist:string;
  2148.   var
  2149.     ii,cc,rc,fc,arow:integer;
  2150.   begin
  2151.     result:='';
  2152.     rc:=RecordSets[sqlresult].recordcount;
  2153.     if rc=0 then exit;
  2154.     fc:=RecordSets[sqlresult].fieldcount;
  2155.     if fc=0 then exit;
  2156.     for arow:=0 to rc-1 do
  2157.       if result='' then
  2158.         result:='['+RecordSets[sqlresult].records[arow].fields[0].value
  2159.       else
  2160.         result:=result+']['+RecordSets[sqlresult].records[arow].fields[0].value;
  2161.     result:=result+']';
  2162.   end;
  2163. begin
  2164.   handled:=false;
  2165.   sqlresult:=SQLDirect(subexpression);
  2166.   if sqlresult>0 then begin
  2167.     subexpressionvalue:=getresultlist;
  2168.     ReleaseRecordset(sqlresult);
  2169.     handled:=true;
  2170.   end;
  2171. end;
  2172.  
  2173. procedure TjanSQL.GroupBy(arecordset: TjanRecordset; grouplist: string);
  2174. var
  2175.   grpidx:array of integer;
  2176.   i,c,fii:integer;
  2177.   g,groups:integer;
  2178.   r,rc,fc:integer;
  2179.   hash:TmwStringHashList;
  2180.   grouper:string;
  2181.   anId,anExId:integer;
  2182.  
  2183.   procedure resetgroup(arecord:integer);
  2184.   var
  2185.     ii,cc:integer;
  2186.   begin
  2187.     for ii:=0 to fc-1 do
  2188.       case arecordset.FFieldFuncs[ii] of
  2189.       tosqlcount:
  2190.         begin
  2191.           arecordset.records[arecord].fields[ii].value:='1';
  2192.           arecordset.records[arecord].counter:=1;
  2193.         end;
  2194.       else
  2195.         begin
  2196.           arecordset.records[arecord].counter:=1;
  2197.         end;
  2198.       end;
  2199.   end;
  2200.  
  2201.   function forcefloat(atext:string):double;
  2202.   begin
  2203.     try
  2204.       result:=strtofloat(atext);
  2205.     except
  2206.       result:=0;
  2207.     end;
  2208.   end;
  2209.  
  2210.   procedure applygroup(arecord,torecord:integer);
  2211.   var
  2212.     ii,cc:integer;
  2213.     s1,s2:string;
  2214.     d1,d2,d3,sum,sum2,dmean:double;
  2215.  
  2216.   begin
  2217.     for ii:=0 to fc-1 do begin
  2218.       s1:=arecordset.records[arecord].fields[ii].value;
  2219.       s2:=arecordset.records[torecord].fields[ii].value;
  2220.       case arecordset.FFieldFuncs[ii] of
  2221.       tosqlcount:
  2222.         begin
  2223.           arecordset.records[torecord].fields[ii].value:=
  2224.             floattostr(forcefloat(s2)+1);
  2225.         end;
  2226.       tosqlMax:
  2227.         begin
  2228.           if forcefloat(s1)>forcefloat(s2) then
  2229.           arecordset.records[torecord].fields[ii].value:=s1;
  2230.         end;
  2231.       tosqlMin:
  2232.         begin
  2233.           if forcefloat(s1)<forcefloat(s2) then
  2234.           arecordset.records[torecord].fields[ii].value:=s1;
  2235.         end;
  2236.       tosqlsum:
  2237.         begin
  2238.           arecordset.records[torecord].fields[ii].value:=
  2239.             floattostr(forcefloat(s2)+forcefloat(s1));
  2240.         end;
  2241.       tosqlavg:
  2242.         begin
  2243.            cc:=arecordset.records[torecord].counter;
  2244.            d1:=forcefloat(s1);
  2245.            d2:=forcefloat(s2);
  2246.            d3:=(d2*cc+d1)/(cc+1);
  2247.            arecordset.records[torecord].fields[ii].value:=
  2248.              floattostr(d3);
  2249.            arecordset.records[torecord].counter:=cc+1;
  2250.         end;
  2251.       tosqlstddev:
  2252.         begin
  2253.            cc:=arecordset.records[torecord].counter;
  2254.            inc(cc);
  2255.            arecordset.records[torecord].counter:=cc;
  2256.            d1:=forcefloat(s1);  // from
  2257.            d2:=forcefloat(s2);  // to
  2258.            sum:=arecordset.records[torecord].fields[ii].sum+d1;
  2259.            arecordset.records[torecord].fields[ii].sum:=sum;
  2260.            sum2:=arecordset.records[torecord].fields[ii].sum2+d1*d1;
  2261.            arecordset.records[torecord].fields[ii].sum2:=sum2;
  2262.            dmean:=sum/cc;
  2263.            d3 := sqrt(Sum2/cc - dmean*dmean);
  2264.            arecordset.records[torecord].fields[ii].value:=
  2265.              floattostr(d3);
  2266.         end;
  2267.       end;
  2268.     end;
  2269.   end;
  2270.  
  2271. begin
  2272.   groups:=0;
  2273.   rc:=arecordset.recordcount;
  2274.   if rc=0 then exit;
  2275.   fc:=arecordset.fieldcount;
  2276.   if fc=0 then exit;
  2277.   // unmark all records
  2278.   for r:=0 to rc-1 do begin
  2279.     arecordset.records[r].mark:=false;
  2280.     arecordset.records[r].counter:=0;
  2281.   end;
  2282.  
  2283.   // no grouplist so only 1 record
  2284.   if grouplist='' then begin
  2285.     resetgroup(0);
  2286.     for r:=1 to rc-1 do begin
  2287.       applygroup(r,0);
  2288.       arecordset.records[r].mark:=true;
  2289.     end;
  2290.     for r:=rc-1 downto 0 do
  2291.       if arecordset.records[r].mark then
  2292.         arecordset.DeleteRecord(r);
  2293.     exit;
  2294.   end;
  2295.   split(grouplist,gen);
  2296.   groups:=gen.count;
  2297.   if groups<>0 then begin
  2298.     setlength(grpidx,groups);
  2299.     for i:=0 to groups-1 do begin
  2300.       fii:=arecordset.IndexOfField(gen[i]);
  2301.       if fii=-1 then exit;
  2302.       grpidx[i]:=fii;
  2303.     end;
  2304.   end;
  2305.  
  2306.   // unmark all records
  2307.   for r:=0 to rc-1 do begin
  2308.     arecordset.records[r].mark:=false;
  2309.     arecordset.records[r].counter:=0;
  2310.   end;
  2311.   try
  2312.     hash:=TmwStringHashList.create(tinyhash,HashSecondaryOne,HashCompare);
  2313.     for r:=0 to rc-1 do begin
  2314.       grouper:='';
  2315.       for g:=0 to groups-1 do
  2316.          grouper:=grouper+arecordset.records[r].fields[grpidx[g]].value+'|';
  2317.       if not hash.Hash(grouper,anId,anExId) then begin
  2318.       // add hash
  2319.         hash.AddString(grouper,r,0);
  2320.         resetgroup(r);
  2321.       end
  2322.       else begin
  2323.         applygroup(r,anId);
  2324.         arecordset.records[r].mark:=true;
  2325.       end;
  2326.     end;
  2327.   finally
  2328.     hash.free;
  2329.   end;
  2330.   for r:=rc-1 downto 0 do
  2331.     if arecordset.records[r].mark then
  2332.       arecordset.DeleteRecord(r);
  2333. end;
  2334.  
  2335.  
  2336. procedure TjanSQL.SortRecordSet(arecordset, From, Count: Integer;
  2337.   orderbylist: string; ascending: boolean);
  2338. var
  2339.   i,c,fii,p:integer;
  2340.   orderby:array of TjanSQLSort;
  2341.   fieldname:string;
  2342. begin
  2343.   split(orderbylist,gen);
  2344.   c:=gen.count;
  2345.   if c=0 then exit;
  2346.   setlength(orderby,c);
  2347.   for i:=0 to c-1 do begin
  2348.     orderby[i].SortAscending:=true;
  2349.     orderby[i].SortNumeric:=false;
  2350.     fieldname:=gen[i];
  2351.     p:=pos('#',fieldname);
  2352.     if p>0 then begin
  2353.       system.Delete(fieldname,p,1);
  2354.       orderby[i].SortNumeric:=true;
  2355.     end;
  2356.     p:=pos('-',fieldname);
  2357.     if p>0 then begin
  2358.       system.Delete(fieldname,p,1);
  2359.       orderby[i].SortAscending:=false;
  2360.     end;
  2361.     p:=pos('+',fieldname);
  2362.     if p>0 then begin
  2363.       system.Delete(fieldname,p,1);
  2364.     end;
  2365.     fii:=recordsets[arecordset].IndexOfField(fieldname);
  2366.     if fii=-1 then exit;
  2367.     orderby[i].FieldIndex:=fii;
  2368.   end;
  2369.   sort(arecordset,from,count,orderby);
  2370. end;
  2371.  
  2372. function TjanSQL.SQLInsertSelect(query: TjanSQLQuery;
  2373.   aline: string): integer;
  2374. var
  2375.   tablename,fieldlist,column:string;
  2376.   columns,values:string;
  2377.   p1,p2,t1,t3,L:integer;
  2378.   i,c:integer;
  2379.   f,fii,fc,r,rc,newr:integer;
  2380.  
  2381.   function havematchingfields:boolean;
  2382.   var
  2383.     ii:integer;
  2384.   begin
  2385.     result:=true;
  2386.     for ii:=0 to fc-1 do
  2387.       if recordsets[t1].IndexOfField(recordsets[t3].fieldnames[ii])<>-1 then exit;
  2388.     result:=false;
  2389.   end;
  2390.  
  2391. begin
  2392.   result:=0;
  2393.   L:=query.FTokens.count;
  2394.   if L<4 then exit;
  2395.   tablename:=query.tokens[2].name;
  2396.   t1:=indexoftable(tablename);
  2397.   err('INSERT INTO: can not find table '+tablename);
  2398.   if t1=0 then exit;
  2399.   for i:=0 to 2 do begin
  2400.     query.Tokens[0].free;
  2401.     query.FTokens.Delete(0);
  2402.   end;
  2403.   t3:=SQLSelect(query,aline,'');
  2404.   rc:=recordsets[t3].recordcount;
  2405.   err('No result records');
  2406.   if rc=0 then exit;
  2407.   fc:=recordsets[t3].fieldcount;
  2408.   err('No result fields');
  2409.   if fc=0 then exit;
  2410.   err('INSERT INTO..SELECT: no mathing fields');
  2411.   if not havematchingfields then exit;
  2412.   for r:=0 to rc-1 do begin
  2413.     newr:=recordsets[t1].addrecord;
  2414.     for f:=0 to fc-1 do begin
  2415.       fii:=recordsets[t1].indexoffield(recordsets[t3].fieldnames[f]);
  2416.       if fii<>-1 then
  2417.         recordsets[t1].records[newr].fields[fii].value:=recordsets[t3].records[r].fields[f].value;
  2418.     end;
  2419.   end;
  2420.   ReleaseRecordset(t3); // JV 25-mar-2002
  2421.   result:=-1;
  2422. end;
  2423.  
  2424.  
  2425. function TjanSQL.InCatalog(value: string): boolean;
  2426. var
  2427.   fn:string;
  2428.   rs:TjanRecordSet;
  2429. begin
  2430.   result:=true;
  2431.   fn:=Fcatalog+'\'+value+'.txt';
  2432.   if fileexists(fn) then exit;
  2433.   result:=false;
  2434. end;
  2435.  
  2436. function TjanSQL.SQLAssign(query: TjanSQLQuery; aline: string): integer;
  2437. var
  2438.   tablename:string;
  2439.   L:integer;
  2440. begin
  2441.   result:=0;
  2442.   L:=query.FTokens.count;
  2443.   err('SELECT: Need at least 4 token');
  2444.   if L<4 then exit;
  2445.   tablename:=query.tokens[1].name;
  2446.   err('SELECT: missing SELECT');
  2447.   if (query.Tokens[2].operator<>tosqlSELECT) then exit;
  2448.   TToken(query.tokens[0]).free;
  2449.   TToken(query.tokens[1]).free;
  2450.   query.FTokens.Delete(0);
  2451.   query.FTokens.Delete(0);
  2452.   result:=SQLSelect(query,aline,tablename);
  2453.   if result>0 then
  2454.     result:=-1;
  2455. end;
  2456.  
  2457. function TjanSQL.SQLSaveTable(query: TjanSQLQuery; aline: string): integer;
  2458. var
  2459.   tablename,fieldlist,column:string;
  2460.   p,t1,L:integer;
  2461. begin
  2462.   result:=0;
  2463.   L:=query.FTokens.count;
  2464.   err('SAVE TABLE: missing tablename');
  2465.   if L<2 then exit;
  2466.   tablename:=query.tokens[1].name;
  2467.   result:=SaveTable(tablename);
  2468. end;
  2469.  
  2470. function TjanSQL.SaveTable(tablename: string): integer;
  2471. var
  2472.   fn:string;
  2473.   idx:integer;
  2474. begin
  2475.   result:=0;
  2476.   err('DROP TABLE: table name missing');
  2477.   if tablename='' then exit;
  2478.   idx:=FRecordSets.IndexOf(tablename);
  2479.   err('SAVE TABLE: unknown table name '+tablename);
  2480.   if idx=-1 then exit;
  2481.   recordsets[idx+1].intermediate:=false;
  2482.   // force persistance
  2483.   recordsets[idx+1].persistent:=true;
  2484.   fn:=Fcatalog+'\'+recordsets[idx+1].name+'.txt';
  2485.   recordsets[idx+1].SaveToFile(fn);
  2486.   recordsets[idx+1].modified:=false;
  2487.   result:=-1;
  2488. end;
  2489.  
  2490. function TjanSQL.SQLReleaseTable(query: TjanSQLQuery;
  2491.   aline: string): integer;
  2492. var
  2493.   tablename:string;
  2494.   p,t1,L:integer;
  2495. begin
  2496.   result:=0;
  2497.   L:=query.FTokens.count;
  2498.   err('SAVE TABLE: missing tablename');
  2499.   if L<2 then exit;
  2500.   tablename:=query.tokens[1].name;
  2501.   result:=ReleaseTable(tablename);
  2502. end;
  2503.  
  2504. function TjanSQL.ReleaseTable(tablename: string): integer;
  2505. var
  2506.   fn:string;
  2507.   idx:integer;
  2508. begin
  2509.   result:=0;
  2510.   err('RELEASE TABLE: table name missing');
  2511.   if tablename='' then exit;
  2512.   idx:=FRecordSets.IndexOf(tablename);
  2513.   err('RELEASE TABLE: unknown table name '+tablename);
  2514.   if idx=-1 then exit;
  2515.   FRecordsets.delete(idx);
  2516.   result:=-1;
  2517. end;
  2518.  
  2519. { TjanRecordset }
  2520.  
  2521. function TjanRecordset.AddField(fieldname,value: string): integer;
  2522. var
  2523.   i,c:integer;
  2524. begin
  2525.   result:=0;
  2526.   if FFieldNames.IndexOf(fieldname)<>-1 then exit;
  2527.   FFieldNames.Append(fieldname);
  2528.   c:=Frecords.Count;
  2529.   if c<>0 then
  2530.     for i:=0 to c-1 do
  2531.       Records[i].AddField(value);
  2532.   result:=-1;
  2533.   modified:=true;  
  2534. end;
  2535.  
  2536. function TjanRecordSet.AddRecord: integer;
  2537. var
  2538.   i,c:integer;
  2539. begin
  2540.   result:= FRecords.Add(Tjanrecord.create);
  2541.   c:=FieldNames.count;
  2542.   if c=0 then exit;
  2543.   for i:=1 to c do
  2544.    records[result].AddField('');
  2545.   modified:=true;
  2546. end;
  2547.  
  2548.  
  2549. procedure TjanRecordset.Clear;
  2550. var
  2551.   i,c:integer;
  2552. begin
  2553.   FfieldNames.Clear;
  2554.   FRecords.Clear;
  2555. end;
  2556.  
  2557. constructor TjanRecordset.create;
  2558. begin
  2559.   FFieldNames:=TStringList.Create;
  2560.   FRecords:=TjanRecordList.Create;
  2561. end;
  2562.  
  2563. function TjanRecordset.DeleteField(index: variant): integer;
  2564. var
  2565.   fi,i,c:integer;
  2566. begin
  2567.   result:=0;
  2568.   if vartype(index)=system.varstring then begin
  2569.     fi:=FFieldNames.IndexOf(index);
  2570.     if fi=-1 then exit;
  2571.   end
  2572.   else
  2573.     fi:=index;
  2574.   if (fi<0) or (fi>=FFieldNames.Count) then exit;
  2575.   c:=recordcount;
  2576.   result:=-1;
  2577.   FFieldNames.Delete(fi);
  2578.   if c=0 then exit;
  2579.   for i:=0 to c-1 do
  2580.     records[i].DeleteField(fi);
  2581.   modified:=true;
  2582. end;
  2583.  
  2584. function TjanRecordSet.DeleteRecord(index: integer): boolean;
  2585. begin
  2586.   result:=false;
  2587.   if (index<0) or (index>=Frecords.count) then exit;
  2588.   Frecords.delete(index);
  2589.   modified:=true;
  2590. end;
  2591.  
  2592. destructor TjanRecordset.destroy;
  2593. begin
  2594.   FFieldNames.Free;
  2595.   Frecords.Free;
  2596.   inherited;
  2597. end;
  2598.  
  2599. function TjanRecordSet.FindFieldValue(fieldindex: integer;
  2600.   fieldvalue: string): integer;
  2601. var
  2602.   i,c:integer;
  2603. begin
  2604.   result:=-1;
  2605.   c:=recordcount;
  2606.   if c=0 then exit;
  2607.   for i:=0 to c-1 do begin
  2608.     if records[i].fields[fieldindex].value=fieldvalue then begin
  2609.       result:=i;
  2610.       exit;
  2611.     end;
  2612.   end;
  2613. end;
  2614.  
  2615. function TjanRecordset.getfieldvalue(index: variant): string;
  2616. var
  2617.   fi:integer;
  2618.   s:string;
  2619.   rec:TjanRecord;
  2620. begin
  2621.   result:='';
  2622.   if FRecordCursor=-1 then exit;
  2623.   if vartype(index)=varstring then begin
  2624.     s:=index;
  2625.     fi:=FFieldNames.IndexOf(s);
  2626.     if fi=-1 then exit;
  2627.   end
  2628.   else
  2629.     fi:=index;
  2630.   rec:=TjanRecord(FRecords.items[FRecordcursor]);
  2631.   result:=rec.fields[fi].value;
  2632. end;
  2633.  
  2634. function TjanRecordset.getrecord(index: integer): TjanRecord;
  2635. begin
  2636.   result:=nil;
  2637.   if FRecords.Count=0 then exit;
  2638.   result:=TjanRecord(FRecords[index]);
  2639. end;
  2640.  
  2641. function TjanRecordset.getrecordcount: integer;
  2642. begin
  2643.   result:=FRecords.Count;
  2644. end;
  2645.  
  2646. function TjanRecordSet.IndexOfField(fieldname: string): integer;
  2647. begin
  2648.   result:=FFieldNames.IndexOf(fieldname);
  2649. end;
  2650.  
  2651. function TjanRecordset.LoadFromFile(filename: string): boolean;
  2652. var
  2653.   gen:TStringList;
  2654.   i,c:integer;
  2655.   rec:TjanRecord;
  2656. begin
  2657.   result:=false;
  2658.   if not fileexists(filename) then exit;
  2659.   Clear;
  2660.   try
  2661.     gen:=Tstringlist.Create;
  2662.     gen.LoadFromFile(filename);
  2663.     c:=gen.count;
  2664.     if c<>0 then begin
  2665.       split(gen[0],FFieldnames);
  2666.       if c>1 then
  2667.         for i:=1 to c-1 do begin
  2668.           rec:=Tjanrecord.create;
  2669.           rec.row:=gen[i];
  2670.           Frecords.Add(rec);
  2671.         end;
  2672.       result:=true;
  2673.     end;
  2674.   finally
  2675.     gen.free;
  2676.   end;
  2677. end;
  2678.  
  2679. function TjanRecordset.SaveToFile(filename: string): boolean;
  2680. var
  2681.   gen:TStringList;
  2682.   i,c:integer;
  2683.  
  2684. begin
  2685.   try
  2686.     gen:=TStringList.Create;
  2687.     gen.append(join(FFieldNames));
  2688.     c:=RecordCount;
  2689.     if c<>0 then
  2690.       for i:=0 to c-1 do
  2691.         gen.append(Records[i].row);
  2692.     gen.SaveToFile(filename);
  2693.   finally
  2694.     gen.free;
  2695.   end;
  2696. end;
  2697.  
  2698. procedure TjanRecordset.setfieldvalue(index: variant; const Value: string);
  2699. var
  2700.   fi:integer;
  2701.   s:string;
  2702.   rec:TjanRecord;
  2703. begin
  2704.   if FRecordCursor=-1 then exit;
  2705.   if vartype(index)=varstring then begin
  2706.     s:=index;
  2707.     fi:=FFieldNames.IndexOf(s);
  2708.     if fi=-1 then exit;
  2709.   end
  2710.   else
  2711.     fi:=index;
  2712.   rec:=TjanRecord(FRecords.items[FRecordcursor]);
  2713.   rec.fields[fi].value:=value;
  2714.   modified:=true;  
  2715. end;
  2716.  
  2717. procedure TjanRecordset.Setname(const Value: string);
  2718. begin
  2719.   Fname := Value;
  2720. end;
  2721.  
  2722. procedure TjanRecordset.Setpersistent(const Value: boolean);
  2723. begin
  2724.   Fpersistent := Value;
  2725. end;
  2726.  
  2727.  
  2728. procedure TjanRecordSet.Setmodified(const Value: boolean);
  2729. begin
  2730.   Fmodified := Value;
  2731. end;
  2732.  
  2733. function TjanRecordSet.getfieldcount: integer;
  2734. begin
  2735.   result:=fieldnames.Count;
  2736. end;
  2737.  
  2738.  
  2739. procedure TjanRecordSet.Setmatchrecord(const Value: integer);
  2740. begin
  2741.   Fmatchrecord := Value;
  2742. end;
  2743.  
  2744. function TjanRecordSet.getLongFieldList: string;
  2745. var
  2746.   i,c:integer;
  2747. begin
  2748.   result:='';
  2749.   c:=FFieldNames.Count;
  2750.   if c=0 then exit;
  2751.   for i:=0 to c-1 do
  2752.     if result='' then
  2753.       result:=name+'.'+FFieldNames[i]
  2754.     else
  2755.       result:=result+';'+name+'.'+FFieldNames[i];
  2756. end;
  2757.  
  2758. function TjanRecordSet.getShortFieldList: string;
  2759. var
  2760.   i,c:integer;
  2761. begin
  2762.   result:='';
  2763.   c:=FFieldNames.Count;
  2764.   if c=0 then exit;
  2765.   for i:=0 to c-1 do
  2766.     if result='' then
  2767.       result:=FFieldNames[i]
  2768.     else
  2769.       result:=result+';'+FFieldNames[i];
  2770. end;
  2771.  
  2772. procedure TjanRecordSet.Setalias(const Value: string);
  2773. begin
  2774.   Falias := Value;
  2775. end;
  2776.  
  2777. procedure TjanRecordSet.Setintermediate(const Value: boolean);
  2778. begin
  2779.   Fintermediate := Value;
  2780. end;
  2781.  
  2782. { TjanRecord }
  2783.  
  2784. procedure TjanRecord.AddField(value: string);
  2785. var
  2786.   f:TjanSQLRecordField;
  2787. begin
  2788.   f:=TjanSQLRecordField.create;
  2789.   f.value:=value;
  2790.   FFields.Add(f);
  2791.  
  2792. end;
  2793.  
  2794. procedure TjanRecord.ClearFields;
  2795. var
  2796.   i,c:integer;
  2797. begin
  2798.   c:=FFields.Count;
  2799.   if c=0 then exit;
  2800.   for i:=0 to c-1 do
  2801.     TjanSQLRecordField(FFields[i]).free;
  2802.   FFields.Clear;
  2803. end;
  2804.  
  2805. constructor TjanRecord.create;
  2806. begin
  2807.   FFields:=TList.Create;
  2808. end;
  2809.  
  2810. function TjanRecord.DeleteField(index: integer): boolean;
  2811. begin
  2812.   result:=false;
  2813.   if (index<0) or (index>=FFields.count) then exit;
  2814.   TjanSQLRecordField(FFields[index]).free;
  2815.   FFields.Delete(index);
  2816.   result:=true;
  2817. end;
  2818.  
  2819. destructor TjanRecord.destroy;
  2820. begin
  2821.   ClearFields;
  2822.   FFields.free;
  2823.   inherited;
  2824. end;
  2825.  
  2826. function TjanRecord.getfield(index: integer): TjanSQLRecordField;
  2827. begin
  2828.   result:=nil;
  2829.   if (index<>-1) and (index<FFields.count) then
  2830.     result:=TjanSQLRecordField(FFields[index])
  2831.   else
  2832.     raise exception.create('fieldcount:'+inttostr(FFields.count));
  2833.  
  2834. end;
  2835.  
  2836. function TjanRecord.getrow: string;
  2837. var
  2838.   i,c:integer;
  2839. begin
  2840.   result:='';
  2841.   for i:=0 to FFields.count-1 do
  2842.    if result='' then
  2843.      result:=TjanSQLRecordField(FFields[i]).value
  2844.    else
  2845.      result:=result+';'+TjanSQLRecordField(FFields[i]).value;
  2846.  
  2847. end;
  2848.  
  2849.  
  2850. procedure TjanRecord.Setcounter(const Value: integer);
  2851. begin
  2852.   Fcounter := Value;
  2853. end;
  2854.  
  2855. procedure TjanRecord.setfield(index: integer; const Value: string);
  2856. begin
  2857.   if (index<>-1) and (index<FFields.count) then
  2858.     TjanSQLRecordField(FFields[index]).value:=value;
  2859.  
  2860. end;
  2861.  
  2862. procedure TjanRecord.Setmark(const Value: boolean);
  2863. begin
  2864.   Fmark := Value;
  2865. end;
  2866.  
  2867. procedure TjanRecord.setrow(const Value: string);
  2868. var
  2869.  i,c:integer;
  2870.  lis:TStringlist;
  2871. begin
  2872.   ClearFields;
  2873.   try
  2874.     lis:=TStringList.create;
  2875.     split(value,lis);
  2876.     c:=lis.count;
  2877.     for i:=0 to c-1 do
  2878.       AddField(lis[i]);
  2879.   finally
  2880.     lis.free;
  2881.   end;
  2882. end;
  2883.  
  2884.  
  2885. { TjanRecordList }
  2886.  
  2887. procedure TjanRecordList.Clear;
  2888. var
  2889.   i,c:integer;
  2890. begin
  2891.   c:=count;
  2892.   if c<>0 then
  2893.     for i:=0 to c-1 do
  2894.       TjanRecord(self.items[i]).free;
  2895.   inherited;
  2896. end;
  2897.  
  2898.  
  2899. procedure TjanRecordList.delete(index: integer);
  2900. begin
  2901.   TjanRecord(items[index]).free;
  2902.   inherited;
  2903.  
  2904. end;
  2905.  
  2906. destructor TjanRecordList.destroy;
  2907. begin
  2908.   Clear;
  2909.   inherited;
  2910. end;
  2911.  
  2912.  
  2913. { TjanRecordSetList }
  2914.  
  2915. procedure TjanRecordSetList.delete(index: integer);
  2916. begin
  2917.   TjanRecordSet(objects[index]).free;
  2918.   inherited;
  2919. end;
  2920.  
  2921. destructor TjanRecordSetList.destroy;
  2922. var
  2923.   i,c:integer;
  2924. begin
  2925.   c:=self.Count;
  2926.   if c>0 then
  2927.    for i:=0 to c-1 do
  2928.      TjanRecordSet(objects[i]).free;
  2929.   inherited;
  2930. end;
  2931.  
  2932.  
  2933. { TjanSQLQuery }
  2934.  
  2935. procedure TjanSQLQuery.ClearTokenList;
  2936. var
  2937.   i,c:Integer;
  2938. begin
  2939.   c:=FTokens.Count;
  2940.   if c=0 then exit;
  2941.   for i:=0 to c-1 do
  2942.     TToken(Ftokens[i]).free;
  2943.   FTokens.clear;
  2944. end;
  2945.  
  2946. constructor TjanSQLQuery.create;
  2947. begin
  2948.   FTokens:=TList.create;
  2949.   FParser:=TjanSQLExpression2.create;
  2950. end;
  2951.  
  2952. destructor TjanSQLQuery.destroy;
  2953. begin
  2954.   ClearTokenList;
  2955.   FTokens.free;
  2956.   FParser.free;
  2957.   inherited;
  2958. end;
  2959.  
  2960. function TjanSQLQuery.getParser: TjanSQLExpression2;
  2961. begin
  2962.   result:=FParser;
  2963. end;
  2964.  
  2965. function TjanSQLQuery.GetToken(index: integer): TToken;
  2966. begin
  2967.   result:=TToken(Ftokens[index]);
  2968. end;
  2969.  
  2970. procedure TjanSQLQuery.SetEngine(const Value: TjanSQL);
  2971. begin
  2972.   FEngine := Value;
  2973. end;
  2974.  
  2975. { TjanSQLCalcField }
  2976.  
  2977. constructor TjanSQLCalcField.create;
  2978. begin
  2979.   FCalc:=TjanSQLExpression2.create;
  2980. end;
  2981.  
  2982. destructor TjanSQLCalcField.destroy;
  2983. begin
  2984.   FCalc.free;
  2985.   inherited;
  2986.  
  2987. end;
  2988.  
  2989. function TjanSQLCalcField.getValue: variant;
  2990. var
  2991.   tl:TStringList;
  2992. begin
  2993.   result:=FCalc.Evaluate;
  2994. end;
  2995.  
  2996. procedure TjanSQLCalcField.Setexpression(const Value: string);
  2997. begin
  2998.   Fexpression := Value;
  2999.   FCalc.Expression:=value;
  3000. end;
  3001.  
  3002. procedure TjanSQLCalcField.SetFieldIndex(const Value: integer);
  3003. begin
  3004.   FFieldIndex := Value;
  3005. end;
  3006.  
  3007. procedure TjanSQLCalcField.Setname(const Value: string);
  3008. begin
  3009.   Fname := Value;
  3010. end;
  3011.  
  3012.  
  3013. { TjanSQLOutput }
  3014.  
  3015. function TjanSQLOutput.AddField: TjanSQLCalcField;
  3016. begin
  3017.   result:=TjanSQLCalcField.create;
  3018.   FFields.Add(result);
  3019. end;
  3020.  
  3021. procedure TjanSQLOutput.ClearFields;
  3022. var
  3023.   i,c:integer;
  3024. begin
  3025.   c:=FFields.count;
  3026.   if c=0 then exit;
  3027.   for i:=0 to c-1 do
  3028.     TjanSQLCalcField(FFields[i]).free;
  3029.   FFields.clear;
  3030. end;
  3031.  
  3032. constructor TjanSQLOutput.create;
  3033. begin
  3034.   FFields:=TList.create;
  3035. end;
  3036.  
  3037. destructor TjanSQLOutput.destroy;
  3038. begin
  3039.   ClearFields;
  3040.   FFields.free;
  3041.   inherited;
  3042. end;
  3043.  
  3044. function TjanSQLOutput.getField(index: integer): TjanSQLCalcField;
  3045. begin
  3046.   result:=nil;
  3047.   if (index<0) or (index>=FFields.count) then exit;
  3048.   result:=TjanSQLCalcField(FFields[index]);
  3049. end;
  3050.  
  3051. function TjanSQLOutput.getFieldCount: integer;
  3052. begin
  3053.   result:=FFields.count;
  3054. end;
  3055.  
  3056. function TjanSQLOutput.getFieldNames: string;
  3057. var
  3058.   i,c:integer;
  3059. begin
  3060.   result:='';
  3061.   c:=FFields.Count;
  3062.   if c=0 then exit;
  3063.   for i:=0 to c-1 do
  3064.     if result='' then
  3065.       result:=Fields[i].name
  3066.     else
  3067.       result:=result+';'+Fields[i].name;
  3068. end;
  3069.  
  3070.  
  3071. { TjanSQLRecordField }
  3072.  
  3073. procedure TjanSQLRecordField.Setcount(const Value: integer);
  3074. begin
  3075.   Fcount := Value;
  3076. end;
  3077.  
  3078. procedure TjanSQLRecordField.Setsum(const Value: double);
  3079. begin
  3080.   Fsum := Value;
  3081. end;
  3082.  
  3083. procedure TjanSQLRecordField.Setsum2(const Value: double);
  3084. begin
  3085.   Fsum2 := Value;
  3086. end;
  3087.  
  3088. procedure TjanSQLRecordField.Setvalue(const Value: variant);
  3089. begin
  3090.   Fvalue := Value;
  3091. end;
  3092.  
  3093. end.
  3094.