zeoslib  UNKNOWN
 All Files
ZSelectSchema.pas
Go to the documentation of this file.
1 {*********************************************************}
2 { }
3 { Zeos Database Objects }
4 { SQL Select Objects and Assembler classes }
5 { }
6 { Originally written by Sergey Seroukhov }
7 { }
8 {*********************************************************}
9 
10 {@********************************************************}
11 { Copyright (c) 1999-2012 Zeos Development Group }
12 { }
13 { License Agreement: }
14 { }
15 { This library is distributed in the hope that it will be }
16 { useful, but WITHOUT ANY WARRANTY; without even the }
17 { implied warranty of MERCHANTABILITY or FITNESS FOR }
18 { A PARTICULAR PURPOSE. See the GNU Lesser General }
19 { Public License for more details. }
20 { }
21 { The source code of the ZEOS Libraries and packages are }
22 { distributed under the Library GNU General Public }
23 { License (see the file COPYING / COPYING.ZEOS) }
24 { with the following modification: }
25 { As a special exception, the copyright holders of this }
26 { library give you permission to link this library with }
27 { independent modules to produce an executable, }
28 { regardless of the license terms of these independent }
29 { modules, and to copy and distribute the resulting }
30 { executable under terms of your choice, provided that }
31 { you also meet, for each linked independent module, }
32 { the terms and conditions of the license of that module. }
33 { An independent module is a module which is not derived }
34 { from or based on this library. If you modify this }
35 { library, you may extend this exception to your version }
36 { of the library, but you are not obligated to do so. }
37 { If you do not wish to do so, delete this exception }
38 { statement from your version. }
39 { }
40 { }
41 { The project web site is located on: }
42 { http://zeos.firmos.at (FORUM) }
43 { http://sourceforge.net/p/zeoslib/tickets/ (BUGTRACKER)}
44 { svn://svn.code.sf.net/p/zeoslib/code-0/trunk (SVN) }
45 { }
46 { http://www.sourceforge.net/projects/zeoslib. }
47 { }
48 { }
49 { Zeos Development Group. }
50 {********************************************************@}
51 
52 unit ZSelectSchema;
53 
54 interface
55 
56 {$I ZParseSql.inc}
57 
58 uses ZClasses, Contnrs, ZCompatibility
59  {$IFDEF WITH_SYSTEMCLASSES},System.Classes{$ENDIF}
60  {$IFDEF WITH_TOBJECTLIST_INLINE}, System.Types{$ENDIF};
61 
62 type
63 
64  {** Case Sensitive/Unsensitive identificator processor. }
65  IZIdentifierConvertor = interface (IZInterface)
66  ['{2EB07B9B-1E96-4A42-8084-6F98D9140B27}']
67 
68  function IsCaseSensitive(const Value: string): Boolean;
69  function IsQuoted(const Value: string): Boolean;
70  function Quote(const Value: string): string;
71  function ExtractQuote(const Value: string): string;
72  end;
73 
74  {** Implements a table reference assembly. }
75  TZTableRef = class (TObject)
76  private
77  FCatalog: string;
78  FSchema: string;
79  FTable: string;
80  FAlias: string;
81  public
82  constructor Create(const Catalog, Schema, Table, Alias: string);
83  function FullName: string;
84 
85  property Catalog: string read FCatalog write FCatalog;
86  property Schema: string read FSchema write FSchema;
87  property Table: string read FTable write FTable;
88  property Alias: string read FAlias write FAlias;
89  end;
90 
91  {** Implements a field reference assembly. }
92  TZFieldRef = class (TObject)
93  private
94  FIsField: Boolean;
95  FCatalog: string;
96  FSchema: string;
97  FTable: string;
98  FField: string;
99  FAlias: string;
100  FTableRef: TZTableRef;
101  FLinked: Boolean;
102  public
103  constructor Create(IsField: Boolean; const Catalog, Schema, Table,
104  Field, Alias: string; TableRef: TZTableRef);
105 
106  property IsField: Boolean read FIsField write FIsField;
107  property Catalog: string read FCatalog write FCatalog;
108  property Schema: string read FSchema write FSchema;
109  property Table: string read FTable write FTable;
110  property Field: string read FField write FField;
111  property Alias: string read FAlias write FAlias;
112  property TableRef: TZTableRef read FTableRef write FTableRef;
113  property Linked: Boolean read FLinked write FLinked;
114  end;
115 
116  {** Defines an interface to select assembly. }
117  IZSelectSchema = interface (IZInterface)
118  ['{3B892975-57E9-4EB7-8DB1-BDDED91E7FBC}']
119 
120  procedure AddField(FieldRef: TZFieldRef);
121  procedure InsertField(Index: Integer; FieldRef: TZFieldRef);
122  procedure DeleteField(FieldRef: TZFieldRef);
123 
124  procedure AddTable(TableRef: TZTableRef);
125 
126  procedure LinkReferences(Convertor: IZIdentifierConvertor);
127 
128  function FindTableByFullName(const Catalog, Schema, Table: string): TZTableRef;
129  function FindTableByShortName(const Table: string): TZTableRef;
130  function FindFieldByShortName(const Field: string): TZFieldRef;
131 
132  function LinkFieldByIndexAndShortName(const ColumnIndex: Integer; const Field: string;
133  const Convertor: IZIdentifierConvertor): TZFieldRef;
134 
135  function GetFieldCount: Integer;
136  function GetTableCount: Integer;
137  function GetField(Index: Integer): TZFieldRef;
138  function GetTable(Index: Integer): TZTableRef;
139 
140  property FieldCount: Integer read GetFieldCount;
141  property Fields[Index: Integer]: TZFieldRef read GetField;
142  property TableCount: Integer read GetTableCount;
143  property Tables[Index: Integer]: TZTableRef read GetTable;
144  end;
145 
146  {** Implements a select assembly. }
147  TZSelectSchema = class (TZAbstractObject, IZSelectSchema)
148  private
149  FFields: TObjectList;
150  FTables: TObjectList;
151 
152  procedure ConvertIdentifiers(Convertor: IZIdentifierConvertor);
153  public
154  constructor Create;
155  destructor Destroy; override;
156 
157  procedure AddField(FieldRef: TZFieldRef);
158  procedure InsertField(Index: Integer; FieldRef: TZFieldRef);
159  procedure DeleteField(FieldRef: TZFieldRef);
160 
161  procedure AddTable(TableRef: TZTableRef);
162 
163  procedure LinkReferences(Convertor: IZIdentifierConvertor);
164 
165  function FindTableByFullName(const Catalog, Schema, Table: string): TZTableRef;
166  function FindTableByShortName(const Table: string): TZTableRef;
167  function FindFieldByShortName(const Field: string): TZFieldRef;
168 
169  function LinkFieldByIndexAndShortName(const ColumnIndex: Integer; const Field: string;
170  const Convertor: IZIdentifierConvertor): TZFieldRef;
171 
172  function GetFieldCount: Integer;
173  function GetTableCount: Integer;
174  function GetField(Index: Integer): TZFieldRef;
175  function GetTable(Index: Integer): TZTableRef;
176 
177  property FieldCount: Integer read GetFieldCount;
178  property Fields[Index: Integer]: TZFieldRef read GetField;
179  property TableCount: Integer read GetTableCount;
180  property Tables[Index: Integer]: TZTableRef read GetTable;
181  end;
182 
183 implementation
184 
185 { TZTableRef }
186 
187 {**
188  Creates a table reference object.
189  @param Catalog a catalog name.
190  @param Schema a schema name.
191  @param Table a table name.
192  @param Alias a table alias.
193 }
194 constructor TZTableRef.Create(const Catalog, Schema, Table, Alias: string);
195 begin
196  FCatalog := Catalog;
197  FSchema := Schema;
198  FTable := Table;
199  FAlias := Alias;
200 end;
201 
202 {**
203  Gets a full database table name.
204  @return a full database table name.
205 }
206 function TZTableRef.FullName: string;
207 begin
208  Result := FCatalog + '.' + FSchema + '.' + FTable;
209 
210  while (Result <> '') and (Result[1] = '.') do
211  Delete(Result, 1, 1);
212 end;
213 
214 { TZFieldRef }
215 
216 {**
217  Creates a field reference object.
218  @param IsField flag which separates table columns from expressions.
219  @param Catalog a catalog name.
220  @param Schema a schema name.
221  @param Table a table name.
222  @param Field a field name.
223  @param Alias a field alias.
224 }
225 constructor TZFieldRef.Create(IsField: Boolean; const Catalog, Schema, Table,
226  Field, Alias: string; TableRef: TZTableRef);
227 begin
228  FIsField := IsField;
229  FCatalog := Catalog;
230  FSchema := Schema;
231  FTable := Table;
232  FField := Field;
233  FAlias := Alias;
234  FTableRef := TableRef;
235  FLinked := False;
236 end;
237 
238 { TZSelectSchema }
239 
240 {**
241  Constructs this assembly object and assignes the main properties.
242 }
243 constructor TZSelectSchema.Create;
244 begin
245  FFields := TObjectList.Create;
246  FTables := TObjectList.Create;
247 end;
248 
249 {**
250  Destroys this object and cleanups the memory.
251 }
252 destructor TZSelectSchema.Destroy;
253 begin
254  FFields.Free;
255  FTables.Free;
256 end;
257 
258 {**
259  Finds a table reference by catalog and table name.
260  @param Catalog a database catalog name.
261  @param Schema a database schema name.
262  @param Table a database table name.
263  @return a found table reference object or <code>null</code> otherwise.
264 }
265 function TZSelectSchema.FindTableByFullName(
266  const Catalog, Schema, Table: string): TZTableRef;
267 var
268  I: Integer;
269  Current: TZTableRef;
270 begin
271  Result := nil;
272 
273  { Looks a table by it's full name. }
274  for I := 0 to FTables.Count - 1 do
275  begin
276  Current := TZTableRef(FTables[I]);
277  if (Current.Schema = Schema) and (Current.Table = Table) then
278  begin
279  Result := Current;
280  Exit;
281  end;
282  end;
283 
284  { Looks a table by it's short name. }
285  for I := 0 to FTables.Count - 1 do
286  begin
287  Current := TZTableRef(FTables[I]);
288  if (Current.Schema = '') and (Current.Table = Table) then
289  begin
290  Result := Current;
291  Exit;
292  end;
293  end;
294 end;
295 
296 {**
297  Finds a table reference by table name or table alias.
298  @param Table a database table name or alias.
299  @return a found table reference object or <code>null</code> otherwise.
300 }
301 function TZSelectSchema.FindTableByShortName(const Table: string): TZTableRef;
302 var
303  I: Integer;
304  Current: TZTableRef;
305 begin
306  Result := nil;
307 
308  { Looks a table by it's alias. }
309  for I := 0 to FTables.Count - 1 do
310  begin
311  Current := TZTableRef(FTables[I]);
312  if Current.Alias = Table then
313  begin
314  Result := Current;
315  Exit;
316  end;
317  end;
318 
319  { Looks a table by it's name. }
320  for I := 0 to FTables.Count - 1 do
321  begin
322  Current := TZTableRef(FTables[I]);
323  if Current.Table = Table then
324  begin
325  Result := Current;
326  Exit;
327  end;
328  end;
329 end;
330 
331 {**
332  Finds a field reference by field name or field alias.
333  @param Field a table field name or alias.
334  @return a found field reference object or <code>null</code> otherwise.
335 }
336 function TZSelectSchema.FindFieldByShortName(const Field: string): TZFieldRef;
337 var
338  I: Integer;
339  Current: TZFieldRef;
340 begin
341  Result := nil;
342  if Field = '' then
343  Exit;
344 
345  { Looks a field by it's alias. }
346  for I := 0 to FFields.Count - 1 do
347  begin
348  Current := TZFieldRef(FFields[I]);
349  if Current.Alias = Field then
350  begin
351  Result := Current;
352  Exit;
353  end;
354  end;
355 
356  { Looks a field by it's name. }
357  for I := 0 to FFields.Count - 1 do
358  begin
359  Current := TZFieldRef(FFields[I]);
360  if Current.Field = Field then
361  begin
362  Result := Current;
363  Exit;
364  end;
365  end;
366 end;
367 
368 {**
369  Links a field reference by index and/or field name or field alias.
370  @param ColumnIndex an index of the column.
371  @param Field a table field name or alias.
372  @return a found field reference object or <code>null</code> otherwise.
373 }
374 function TZSelectSchema.LinkFieldByIndexAndShortName(const ColumnIndex: Integer;
375  const Field: string; const Convertor: IZIdentifierConvertor): TZFieldRef;
376 var
377  I: Integer;
378  Current: TZFieldRef;
379 begin
380  Result := nil;
381  if Field = '' then
382  Exit;
383 
384  { Looks by field index. }
385  if (ColumnIndex > 0) and (ColumnIndex <= FFields.Count) then
386  begin
387  Current := TZFieldRef(FFields[ColumnIndex - 1]);
388  if not Current.Linked
389  //note http://sourceforge.net/p/zeoslib/tickets/101/
390  and ((Current.Alias = Field) or (Current.Field = Field) or (Current.Field = Convertor.Quote(Field))) then
391  begin
392  Result := Current;
393  Result.Linked := True;
394  Exit;
395  end;
396  end;
397 
398  { Looks a field by it's alias. }
399  for I := 0 to FFields.Count - 1 do
400  begin
401  Current := TZFieldRef(FFields[I]);
402  if not Current.Linked and ((Current.Alias = Field) or (Current.Alias = Convertor.Quote(Field))) then
403  begin
404  Result := Current;
405  Result.Linked := True;
406  Exit;
407  end;
408  end;
409 
410  { Looks a field by field and table aliases. }
411  for I := 0 to FFields.Count - 1 do
412  begin
413  Current := TZFieldRef(FFields[I]);
414  if not Current.Linked and Assigned(Current.TableRef)
415  and (((Current.TableRef.Alias + '.' + Current.Field) = Field)
416  or (((Current.TableRef.Table + '.' + Current.Field) = Field))) then
417  begin
418  Result := Current;
419  Result.Linked := True;
420  Exit;
421  end;
422  end;
423 
424  { Looks a field by it's name. }
425  for I := 0 to FFields.Count - 1 do
426  begin
427  Current := TZFieldRef(FFields[I]);
428  if not Current.Linked and (Current.Field = Field) then
429  begin
430  Result := Current;
431  Result.Linked := True;
432  Exit;
433  end;
434  end;
435 end;
436 
437 {**
438  Convert all table and field identifiers..
439  @param Convertor an identifier convertor.
440 }
441 procedure TZSelectSchema.ConvertIdentifiers(Convertor: IZIdentifierConvertor);
442 var
443  I: Integer;
444  function ExtractNeedlessQuote(Value : String) : String;
445  var
446  tempstring: String;
447  begin
448  tempstring := Convertor.ExtractQuote(Value);
449  if Convertor.IsCaseSensitive(tempstring) then
450  result := Value
451  else
452  result := tempstring;
453  end;
454 
455 begin
456  if Convertor = nil then Exit;
457 
458  for I := 0 to FFields.Count - 1 do
459  begin
460  with TZFieldRef(FFields[I]) do
461  begin
462  Catalog := ExtractNeedlessQuote(Catalog);
463  Schema := ExtractNeedlessQuote(Schema);
464  Table := ExtractNeedlessQuote(Table);
465  Field := ExtractNeedlessQuote(Field);
466  Alias := ExtractNeedlessQuote(Alias);
467  end;
468  end;
469 
470  for I := 0 to FTables.Count - 1 do
471  begin
472  with TZTableRef(FTables[I]) do
473  begin
474  Catalog := ExtractNeedlessQuote(Catalog);
475  Schema := ExtractNeedlessQuote(Schema);
476  Table := ExtractNeedlessQuote(Table);
477  Alias := ExtractNeedlessQuote(Alias);
478  end;
479  end;
480 end;
481 
482 {**
483  Links references between fields and tables.
484  @param Convertor an identifier convertor.
485 }
486 procedure TZSelectSchema.LinkReferences(Convertor: IZIdentifierConvertor);
487 var
488  I, J: Integer;
489  FieldRef: TZFieldRef;
490  TableRef: TZTableRef;
491  TempFields: TObjectList;
492 begin
493  ConvertIdentifiers(Convertor);
494  TempFields := FFields;
495  FFields := TObjectList.Create;
496 
497  try
498  for I := 0 to TempFields.Count - 1 do
499  begin
500  FieldRef := TZFieldRef(TempFields[I]);
501  TableRef := nil;
502 
503  if not FieldRef.IsField then
504  begin
505  FFields.Add(TZFieldRef.Create(FieldRef.IsField, FieldRef.Catalog,
506  FieldRef.Schema, FieldRef.Table, FieldRef.Field, FieldRef.Alias,
507  FieldRef.TableRef));
508  Continue;
509  end
510  else if (FieldRef.Schema <> '') and (FieldRef.Table <> '') then
511  begin
512  TableRef := FindTableByFullName(FieldRef.Catalog, FieldRef.Schema,
513  FieldRef.Table);
514  end
515  else if FieldRef.Table <> '' then
516  TableRef := FindTableByShortName(FieldRef.Table)
517  else if FieldRef.Field = '*' then
518  begin
519  { Add all fields from all tables. }
520  for J := 0 to FTables.Count - 1 do
521  begin
522  with TZTableRef(FTables[J]) do
523  begin
524  FFields.Add(TZFieldRef.Create(True, Catalog, Schema,
525  Table, '*', '', TZTableRef(FTables[J])));
526  end;
527  end;
528  Continue;
529  end;
530 
531  if TableRef <> nil then
532  begin
533  FFields.Add(TZFieldRef.Create(True, TableRef.Catalog, TableRef.Schema,
534  TableRef.Table, FieldRef.Field, FieldRef.Alias, TableRef));
535  end
536  else
537  begin
538  FFields.Add(TZFieldRef.Create(True, FieldRef.Catalog, FieldRef.Schema,
539  FieldRef.Table, FieldRef.Field, FieldRef.Alias, TableRef));
540  end;
541  end;
542  finally
543  TempFields.Free;
544  end;
545 end;
546 
547 {**
548  Adds a new field to this select schema.
549  @param FieldRef a field reference object.
550 }
551 procedure TZSelectSchema.AddField(FieldRef: TZFieldRef);
552 begin
553  FFields.Add(FieldRef);
554 end;
555 
556 {**
557  Inserts a new field to this select schema.
558  @param Index an index where to insert a new field reference.
559  @param FieldRef a field reference object.
560 }
561 procedure TZSelectSchema.InsertField(Index: Integer; FieldRef: TZFieldRef);
562 begin
563  FFields.Insert(Index, FieldRef);
564 end;
565 
566 {**
567  Deletes a field from this select schema.
568  @param FieldRef a field reference object.
569 }
570 procedure TZSelectSchema.DeleteField(FieldRef: TZFieldRef);
571 begin
572  FFields.Remove(FieldRef);
573 end;
574 
575 {**
576  Adds a new table to this select schema.
577  @param TableRef a table reference object.
578 }
579 procedure TZSelectSchema.AddTable(TableRef: TZTableRef);
580 begin
581  FTables.Add(TableRef);
582 end;
583 
584 {**
585  Gets a field reference by index.
586  @param Index an index of the reference.
587  @returns a pointer to the field reference.
588 }
589 function TZSelectSchema.GetField(Index: Integer): TZFieldRef;
590 begin
591  Result := TZFieldRef(FFields[Index]);
592 end;
593 
594 {**
595  Gets a count of field references.
596  @returns a count of field references.
597 }
598 function TZSelectSchema.GetFieldCount: Integer;
599 begin
600  Result := FFields.Count;
601 end;
602 
603 {**
604  Gets a table reference by index.
605  @param Index an index of the reference.
606  @returns a pointer to the table reference.
607 }
608 function TZSelectSchema.GetTable(Index: Integer): TZTableRef;
609 begin
610  Result := TZTableRef(FTables[Index]);
611 end;
612 
613 {**
614  Gets a count of table references.
615  @returns a count of table references.
616 }
617 function TZSelectSchema.GetTableCount: Integer;
618 begin
619  Result := FTables.Count;
620 end;
621 
622 end.
623