zeoslib  UNKNOWN
 All Files
ZDbcUtils.pas
Go to the documentation of this file.
1 {*********************************************************}
2 { }
3 { Zeos Database Objects }
4 { Database Connectivity Functions }
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 ZDbcUtils;
53 
54 interface
55 
56 {$I ZDbc.inc}
57 
58 uses
59  Types, Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils, Contnrs,
60  ZCompatibility, ZDbcIntfs, ZDbcResultSetMetadata;
61 
62 {**
63  Resolves a connection protocol and raises an exception with protocol
64  is not supported.
65  @param Url an initial database URL.
66  @param SuupportedProtocols a driver's supported subprotocols.
67 }
68 function ResolveConnectionProtocol(Url: string;
69  SupportedProtocols: TStringDynArray): string;
70 
71 {**
72  Resolves a database URL and fills the database connection parameters.
73  @param Url an initial database URL.
74  @param Info an initial info parameters.
75  @param HostName a name of the database host.
76  @param Port a port number.
77  @param Database a database name.
78  @param UserName a name of the database user.
79  @param Password a user's password.
80  @param ResutlInfo a result info parameters.
81 }
82 procedure ResolveDatabaseUrl(const Url: string; Info: TStrings;
83  var HostName: string; var Port: Integer; var Database: string;
84  var UserName: string; var Password: string; ResultInfo: TStrings);
85 
86 {**
87  Checks is the convertion from one type to another type allowed.
88  @param InitialType an initial data type.
89  @param ResultType a result data type.
90  @return <code>True</code> if convertion is allowed
91  or <code>False</code> otherwise.
92 }
93 function CheckConvertion(InitialType: TZSQLType; ResultType: TZSQLType): Boolean;
94 
95 {**
96  Defines a name of the column type.
97  @param ColumnType a type of the column.
98  @return a name of the specified type.
99 }
100 function DefineColumnTypeName(ColumnType: TZSQLType): string;
101 
102 {**
103  Raises a copy of the given exception.
104  @param E an exception to be raised.
105 }
106 procedure RaiseSQLException(E: Exception);
107 
108 {**
109  Copies column information objects from one object list to another one.
110  @param FromList the source object list.
111  @param ToList the destination object list.
112 }
113 procedure CopyColumnsInfo(FromList: TObjectList; ToList: TObjectList);
114 
115 {**
116  Defines a statement specific parameter.
117  @param Statement a statement interface reference.
118  @param ParamName a name of the parameter.
119  @param Default a parameter default value.
120  @return a parameter value or default if nothing was found.
121 }
122 function DefineStatementParameter(Statement: IZStatement; const ParamName: string;
123  const Default: string): string;
124 
125 {**
126  ToLikeString returns the given string or if the string is empty it returns '%'
127  @param Value the string
128  @return given Value or '%'
129 }
130 function ToLikeString(const Value: string): string;
131 
132 {**
133  GetSQLHexString returns a valid x'..' database understandable String from
134  binary data
135  @param Value the ansistring-pointer to the binary data
136  @param Len then length of the binary Data
137  @param ODBC a boolean if output result should be with a starting 0x...
138  @returns a valid hex formated unicode-safe string
139 }
140 function GetSQLHexWideString(Value: PAnsiChar; Len: Integer; ODBC: Boolean = False): ZWideString;
141 function GetSQLHexAnsiString(Value: PAnsiChar; Len: Integer; ODBC: Boolean = False): RawByteString;
142 function GetSQLHexString(Value: PAnsiChar; Len: Integer; ODBC: Boolean = False): String;
143 
144 {**
145  Returns a FieldSize in Bytes dependend to the FieldType and CharWidth
146  @param <code>TZSQLType</code> the Zeos FieldType
147  @param <code>Integer</code> the Current given FieldLength
148  @param <code>Integer</code> the Current CountOfByte/Char
149  @param <code>Boolean</code> does the Driver returns the FullSizeInBytes
150  @returns <code>Integer</code> the count of AnsiChars for Field.Size * SizeOf(Char)
151 }
152 function GetFieldSize(const SQLType: TZSQLType;ConSettings: PZConSettings;
153  const Precision, CharWidth: Integer; DisplaySize: PInteger = nil;
154  SizeInBytes: Boolean = False): Integer;
155 
156 function WideStringStream(const AString: WideString): TStream;
157 
158 implementation
159 
160 uses ZMessages, ZSysUtils, ZEncoding;
161 
162 {**
163  Resolves a connection protocol and raises an exception with protocol
164  is not supported.
165  @param Url an initial database URL.
166  @param SupportedProtocols a driver's supported subprotocols.
167 }
168 function ResolveConnectionProtocol(Url: string;
169  SupportedProtocols: TStringDynArray): string;
170 var
171  I: Integer;
172  Protocol: string;
173  Index: Integer;
174 begin
175  Result := '';
176 
177  Index := FirstDelimiter(':', Url);
178  if Index > 0 then
179  Protocol := Copy(Url, Index + 1, Length(Url) - Index)
180  else
181  Protocol := '';
182  Index := FirstDelimiter(':', Protocol);
183  if Index > 1 then
184  Protocol := Copy(Protocol, 1, Index - 1)
185  else
186  Protocol := '';
187 
188  if Protocol = '' then
189  raise EZSQLException.Create(Format(SIncorrectConnectionURL, [Url]));
190 
191  for I := Low(SupportedProtocols) to High(SupportedProtocols) do
192  begin
193  if SupportedProtocols[I] = Protocol then
194  begin
195  Result := Protocol;
196  Break;
197  end;
198  end;
199 
200  if Result = '' then
201  raise EZSQLException.Create(Format(SUnsupportedProtocol, [Protocol]));
202 end;
203 
204 {**
205  Resolves a database URL and fills the database connection parameters.
206  @param Url an initial database URL.
207  @param Info an initial info parameters.
208  @param HostName a name of the database host.
209  @param Port a port number.
210  @param Database a database name.
211  @param UserName a name of the database user.
212  @param Password a user's password.
213  @param ResutlInfo a result info parameters.
214 }
215 procedure ResolveDatabaseUrl(const Url: string; Info: TStrings;
216  var HostName: string; var Port: Integer; var Database: string;
217  var UserName: string; var Password: string; ResultInfo: TStrings);
218 var
219  Temp: string;
220 begin
221  { assign URL first -> define all out out params }
222  {A correct builded URL exports all these Params if they are expected!}
223  DriverManager.ResolveDatabaseUrl(URL, HostName, Port, DataBase, UserName, Password, ResultInfo);
224 
225  { Retrieves non special-escaped-parameters }
226  Temp := Url;
227  while FirstDelimiter('?', Temp) > 0 do //Get all aditional Parameters
228  Temp := Copy(Temp, FirstDelimiter('?', Temp)+1, Length(Temp));
229  PutSplitString(ResultInfo, Temp, ';'); //overrides all Strings
230  ResultInfo.Text := StringReplace(ResultInfo.Text, #9, ';', [rfReplaceAll]); //unescape the #9 char
231 
232  if Assigned(Info) then //isn't that strange? (Shouldn't we pick out double-values?)
233  Resultinfo.AddStrings(Info);//All possible PWD/Password and UID/UserName are aviable now, but for what? And the can also be doubled!
234 
235  { Redefines user name if not avialble in the URL}
236  if UserName = '' then //Priority 1: URL.UserName
237  begin
238  UserName := ResultInfo.Values['UID']; //Priority 2: Info-UID
239  if UserName = '' then
240  UserName := ResultInfo.Values['username']; //Priority 3: Info-username
241  end;
242 
243  { Redefines user password if not avialble in the URL }
244  if Password = '' then //Priority 1: URL.Password
245  begin
246  Password := ResultInfo.Values['PWD']; //Priority 2: Info-PWD
247  if Password = '' then
248  Password := ResultInfo.Values['password']; //Priority 3: Info-password
249  end;
250 end;
251 
252 {**
253  Checks is the convertion from one type to another type allowed.
254  @param InitialType an initial data type.
255  @param ResultType a result data type.
256  @return <code>True</code> if convertion is allowed
257  or <code>False</code> otherwise.
258 }
259 function CheckConvertion(InitialType: TZSQLType; ResultType: TZSQLType): Boolean;
260 begin
261  case ResultType of
262  stBoolean, stByte, stShort, stInteger,
263  stLong, stFloat, stDouble, stBigDecimal:
264  Result := InitialType in [stBoolean, stByte, stShort, stInteger,
265  stLong, stFloat, stDouble, stBigDecimal, stString, stUnicodeString];
266  stString, stUnicodeString:
267  Result := True;
268  stBytes:
269  Result := InitialType in [stString, stUnicodeString, stBytes, stGUID,
270  stAsciiStream, stUnicodeStream, stBinaryStream];
271  stTimestamp:
272  Result := InitialType in [stString, stUnicodeString, stDate, stTime, stTimestamp];
273  stDate:
274  Result := InitialType in [stString, stUnicodeString, stDate, stTimestamp];
275  stTime:
276  Result := InitialType in [stString, stUnicodeString, stTime, stTimestamp];
277  else
278  Result := (ResultType = InitialType) and (InitialType <> stUnknown);
279  end;
280 end;
281 
282 {**
283  Defines a name of the column type.
284  @param ColumnType a type of the column.
285  @return a name of the specified type.
286 }
287 function DefineColumnTypeName(ColumnType: TZSQLType): string;
288 begin
289  case ColumnType of
290  stBoolean:
291  Result := 'Boolean';
292  stByte:
293  Result := 'Byte';
294  stShort:
295  Result := 'Short';
296  stInteger:
297  Result := 'Integer';
298  stLong:
299  Result := 'Long';
300  stFloat:
301  Result := 'Float';
302  stDouble:
303  Result := 'Double';
304  stBigDecimal:
305  Result := 'BigDecimal';
306  stString:
307  Result := 'String';
308  stUnicodeString:
309  Result := 'UnicodeString';
310  stBytes:
311  Result := 'Bytes';
312  stGUID:
313  Result := 'GUID';
314  stDate:
315  Result := 'Date';
316  stTime:
317  Result := 'Time';
318  stTimestamp:
319  Result := 'Timestamp';
320  stAsciiStream:
321  Result := 'AsciiStream';
322  stUnicodeStream:
323  Result := 'UnicodeStream';
324  stBinaryStream:
325  Result := 'BinaryStream';
326  else
327  Result := 'Unknown';
328  end;
329 end;
330 
331 {**
332  Raises a copy of the given exception.
333  @param E an exception to be raised.
334 }
335 procedure RaiseSQLException(E: Exception);
336 begin
337  if E is EZSQLException then
338  begin
339  raise EZSQLException.CreateClone(EZSQLException(E));
340  end
341  else
342  begin
343  raise EZSQLException.Create(E.Message);
344  end;
345 end;
346 
347 {**
348  Copies column information objects from one object list to another one.
349  @param FromList the source object list.
350  @param ToList the destination object list.
351 }
352 procedure CopyColumnsInfo(FromList: TObjectList; ToList: TObjectList);
353 var
354  I: Integer;
355  Current: TZColumnInfo;
356  ColumnInfo: TZColumnInfo;
357 begin
358  for I := 0 to FromList.Count - 1 do
359  begin
360  Current := TZColumnInfo(FromList[I]);
361  ColumnInfo := TZColumnInfo.Create;
362 
363  ColumnInfo.AutoIncrement := Current.AutoIncrement;
364  ColumnInfo.CaseSensitive := Current.CaseSensitive;
365  ColumnInfo.Searchable := Current.Searchable;
366  ColumnInfo.Currency := Current.Currency;
367  ColumnInfo.Nullable := Current.Nullable;
368  ColumnInfo.Signed := Current.Signed;
369  ColumnInfo.ColumnDisplaySize := Current.ColumnDisplaySize;
370  ColumnInfo.ColumnLabel := Current.ColumnLabel;
371  ColumnInfo.ColumnName := Current.ColumnName;
372  ColumnInfo.SchemaName := Current.SchemaName;
373  ColumnInfo.Precision := Current.Precision;
374  ColumnInfo.Scale := Current.Scale;
375  ColumnInfo.TableName := Current.TableName;
376  ColumnInfo.CatalogName := Current.CatalogName;
377  ColumnInfo.ColumnType := Current.ColumnType;
378  ColumnInfo.ReadOnly := Current.ReadOnly;
379  ColumnInfo.Writable := Current.Writable;
380  ColumnInfo.DefinitelyWritable := Current.DefinitelyWritable;
381 
382  ToList.Add(ColumnInfo);
383  end;
384 end;
385 
386 {**
387  Defines a statement specific parameter.
388  @param Statement a statement interface reference.
389  @param ParamName a name of the parameter.
390  @param Default a parameter default value.
391  @return a parameter value or default if nothing was found.
392 }
393 function DefineStatementParameter(Statement: IZStatement; const ParamName: string;
394  const Default: string): string;
395 begin
396  Result := Statement.GetParameters.Values[ParamName];
397  if Result = '' then
398  Result := Statement.GetConnection.GetParameters.Values[ParamName];
399  if Result = '' then
400  Result := Default;
401 end;
402 
403 {**
404  ToLikeString returns the given string or if the string is empty it returns '%'
405  @param Value the string
406  @return given Value or '%'
407 }
408 function ToLikeString(const Value: string): string;
409 begin
410  if Value = '' then
411  Result := '%'
412  else
413  Result := Value;
414 end;
415 
416 {**
417  GetSQLHexString returns a valid x'..' database understandable String from
418  binary data
419  @param Value the ansistring-pointer to the binary data
420  @param Length then length of the binary Data
421  @param ODBC a boolean if output result should be with a starting 0x...
422  @returns a valid hex formated unicode-safe string
423 }
424 
425 function GetSQLHexWideString(Value: PAnsiChar; Len: Integer; ODBC: Boolean = False): ZWideString;
426 var
427  HexVal: AnsiString;
428 begin
429  SetLength(HexVal,Len * 2 );
430  BinToHex(Value, PAnsiChar(HexVal), Len);
431 
432  if ODBC then
433  Result := '0x'+ZWideString(HexVal)
434  else
435  Result := 'x'#39+ZWideString(HexVal)+#39;
436 end;
437 
438 function GetSQLHexAnsiString(Value: PAnsiChar; Len: Integer; ODBC: Boolean = False): RawByteString;
439 var
440  HexVal: RawByteString;
441 begin
442  SetLength(HexVal,Len * 2 );
443  BinToHex(Value, PAnsiChar(HexVal), Len);
444 
445  if ODBC then
446  Result := '0x'+HexVal
447  else
448  Result := 'x'#39+HexVal+#39;
449 end;
450 
451 function GetSQLHexString(Value: PAnsiChar; Len: Integer; ODBC: Boolean = False): String;
452 begin
453  {$IFDEF UNICODE}
454  Result := GetSQLHexWideString(Value, Len, ODBC);
455  {$ELSE}
456  Result := GetSQLHexAnsiString(Value, Len, ODBC);
457  {$ENDIF}
458 end;
459 
460 {**
461  Returns a FieldSize in Bytes dependend to the FieldType and CharWidth
462  @param <code>TZSQLType</code> the Zeos FieldType
463  @param <code>Integer</code> the Current given FieldLength
464  @param <code>Integer</code> the Current CountOfByte/Char
465  @param <code>Boolean</code> does the Driver returns the FullSizeInBytes
466  @returns <code>Integer</code> the count of AnsiChars for Field.Size * SizeOf(Char)
467 }
468 function GetFieldSize(const SQLType: TZSQLType; ConSettings: PZConSettings;
469  const Precision, CharWidth: Integer; DisplaySize: PInteger = nil;
470  SizeInBytes: Boolean = False): Integer;
471 var
472  TempPrecision: Integer;
473 begin
474  if ( SQLType in [stString, stUnicodeString] ) and ( Precision <> 0 )then
475  begin
476  if SizeInBytes then
477  TempPrecision := Precision div CharWidth
478  else
479  TempPrecision := Precision;
480 
481  if Assigned(DisplaySize) then
482  DisplaySize^ := TempPrecision;
483 
484  if SQLType = stString then
485  //the RowAccessor assumes SizeOf(Char)*Precision+SizeOf(Char)
486  //the Field assumes Precision*SizeOf(Char)
487  {$IFDEF UNICODE}
488  if ConSettings.ClientCodePage.CharWidth >= 2 then //All others > 3 are UTF8
489  Result := TempPrecision * 2 //add more mem for a reserved thirt byte
490  else //two and one byte AnsiChars are one WideChar
491  Result := TempPrecision
492  {$ELSE}
493  if ( ConSettings.CPType = cCP_UTF8 ) or (ConSettings.CTRL_CP = zCP_UTF8) then
494  Result := TempPrecision * 4
495  else
496  Result := TempPrecision * CharWidth
497  {$ENDIF}
498  else //stUnicodeString
499  //UTF8 can pickup LittleEndian/BigEndian 4 Byte Chars
500  //the RowAccessor assumes 2*Precision+2!
501  //the Field assumes 2*Precision ??Does it?
502  if CharWidth > 2 then
503  Result := TempPrecision * 2
504  else
505  Result := TempPrecision;
506  end
507  else
508  Result := Precision;
509 end;
510 
511 function WideStringStream(const AString: WideString): TStream;
512 begin
513  Result := TMemoryStream.Create;
514  Result.Write(PWideChar(AString)^, Length(AString)*2);
515  Result.Position := 0;
516 end;
517 
518 
519 end.
520