|
From: <fas...@us...> - 2007-05-13 21:20:18
|
Revision: 775
http://svn.sourceforge.net/instantobjects/revision/?rev=775&view=rev
Author: fastbike2
Date: 2007-05-13 14:20:15 -0700 (Sun, 13 May 2007)
Log Message:
-----------
Added new MySQL Catalog
Added Paths:
-----------
trunk/Source/Catalogs/MySQL/
trunk/Source/Catalogs/MySQL/D2007/
trunk/Source/Catalogs/MySQL/D2007/IOMySQLCatalog.dpk
trunk/Source/Catalogs/MySQL/D7/
trunk/Source/Catalogs/MySQL/D7/IOMySQLCatalog.dpk
trunk/Source/Catalogs/MySQL/InstantMySQLCatalog.pas
Added: trunk/Source/Catalogs/MySQL/D2007/IOMySQLCatalog.dpk
===================================================================
--- trunk/Source/Catalogs/MySQL/D2007/IOMySQLCatalog.dpk (rev 0)
+++ trunk/Source/Catalogs/MySQL/D2007/IOMySQLCatalog.dpk 2007-05-13 21:20:15 UTC (rev 775)
@@ -0,0 +1,37 @@
+package IOMySQLCatalog;
+
+{$R *.res}
+{$ALIGN 8}
+{$ASSERTIONS ON}
+{$BOOLEVAL OFF}
+{$DEBUGINFO ON}
+{$EXTENDEDSYNTAX ON}
+{$IMPORTEDDATA ON}
+{$IOCHECKS ON}
+{$LOCALSYMBOLS ON}
+{$LONGSTRINGS ON}
+{$OPENSTRINGS ON}
+{$OPTIMIZATION ON}
+{$OVERFLOWCHECKS OFF}
+{$RANGECHECKS OFF}
+{$REFERENCEINFO ON}
+{$SAFEDIVIDE OFF}
+{$STACKFRAMES OFF}
+{$TYPEDADDRESS ON}
+{$VARSTRINGCHECKS ON}
+{$WRITEABLECONST OFF}
+{$MINENUMSIZE 1}
+{$IMAGEBASE $400000}
+{$DESCRIPTION 'InstantObjects Catalog for MySQL (Delphi 2007)'}
+{$LIBSUFFIX '_D11'}
+{$RUNONLY}
+{$IMPLICITBUILD OFF}
+
+requires
+ rtl,
+ IOCore;
+
+contains
+ InstantMySQLCatalog in '..\InstantMySQLCatalog.pas';
+
+end.
Added: trunk/Source/Catalogs/MySQL/D7/IOMySQLCatalog.dpk
===================================================================
--- trunk/Source/Catalogs/MySQL/D7/IOMySQLCatalog.dpk (rev 0)
+++ trunk/Source/Catalogs/MySQL/D7/IOMySQLCatalog.dpk 2007-05-13 21:20:15 UTC (rev 775)
@@ -0,0 +1,37 @@
+package IOMySQLCatalog;
+
+{$R *.res}
+{$ALIGN 8}
+{$ASSERTIONS ON}
+{$BOOLEVAL OFF}
+{$DEBUGINFO ON}
+{$EXTENDEDSYNTAX ON}
+{$IMPORTEDDATA ON}
+{$IOCHECKS ON}
+{$LOCALSYMBOLS ON}
+{$LONGSTRINGS ON}
+{$OPENSTRINGS ON}
+{$OPTIMIZATION ON}
+{$OVERFLOWCHECKS OFF}
+{$RANGECHECKS OFF}
+{$REFERENCEINFO ON}
+{$SAFEDIVIDE OFF}
+{$STACKFRAMES OFF}
+{$TYPEDADDRESS ON}
+{$VARSTRINGCHECKS ON}
+{$WRITEABLECONST OFF}
+{$MINENUMSIZE 1}
+{$IMAGEBASE $400000}
+{$DESCRIPTION 'InstantObjects Catalog for MySQL'}
+{$LIBSUFFIX '_D7'}
+{$RUNONLY}
+{$IMPLICITBUILD OFF}
+
+requires
+ rtl,
+ IOCore;
+
+contains
+ InstantMySQLCatalog in '..\InstantMySQLCatalog.pas';
+
+end.
Added: trunk/Source/Catalogs/MySQL/InstantMySQLCatalog.pas
===================================================================
--- trunk/Source/Catalogs/MySQL/InstantMySQLCatalog.pas (rev 0)
+++ trunk/Source/Catalogs/MySQL/InstantMySQLCatalog.pas 2007-05-13 21:20:15 UTC (rev 775)
@@ -0,0 +1,342 @@
+(*
+ * InstantObjects DBEvolver Support
+ * MySQL Catalog
+ *)
+
+(* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is: InstantObjects DBEvolver Support
+ *
+ * The Initial Developer of the Original Code is: David Moorhouse
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ *
+ * ***** END LICENSE BLOCK ***** *)
+
+ // TODO: separate out the MysQL4.x and MYSQl 5.x
+ // TODO: not sure if different features need supporting for different db engine options e.g. MyISAM, InnoDB, etc
+
+unit InstantMySQLCatalog;
+
+{$IFDEF LINUX}
+{$I '../../InstantDefines.inc'}
+{$ELSE}
+{$I '..\..\InstantDefines.inc'}
+{$ENDIF}
+
+interface
+
+uses
+ InstantPersistence, InstantBrokers, InstantMetadata, InstantTypes;
+
+type
+ // A TInstantCatalog that reads catalog information from a MySQL
+ // database. Can be used with a SQL broker that accesses MySQL databases.
+ TInstantMySQLCatalog = class(TInstantSQLBrokerCatalog)
+ private
+ procedure AddFieldMetadatas(TableMetadata: TInstantTableMetadata);
+ procedure AddIndexMetadatas(TableMetadata: TInstantTableMetadata);
+ procedure AddTableMetadatas(TableMetadatas: TInstantTableMetadatas);
+ // Returns True if the TInstantDataType value that matches the supplied
+ // ColumnType. If more than one datatypes apply, alternate data types are
+ // returned in AlternateDataTypes, otherwise AlternateDataTypes is [] on exit.
+ function ColumnTypeToDataType(const ColumnType: string; out DataType:
+ TInstantDataType; out AlternateDataTypes: TInstantDataTypes): Boolean;
+ function GetSelectFieldsSQL(const ATableName: string): string;
+ function GetSelectIndexesSQL(const ATableName: string): string;
+ function GetSelectTablesSQL: string;
+ public
+ procedure InitTableMetadatas(ATableMetadatas: TInstantTableMetadatas);
+ override;
+ end;
+
+implementation
+
+uses
+ SysUtils, Classes, DB, InstantConsts, StrUtils;
+
+{ TInstantMySQLCatalog }
+
+procedure TInstantMySQLCatalog.AddIndexMetadatas(TableMetadata: TInstantTableMetadata);
+var
+ Indexes: TDataSet;
+ IndexMetadata: TInstantIndexMetadata;
+ IndexFieldList: TStrings;
+ KeyName: string;
+begin
+ IndexFieldList := TStringList.Create;
+ Indexes := Broker.AcquireDataSet(GetSelectIndexesSQL(TableMetadata.Name));
+ try
+ Indexes.Open;
+ try
+ while not Indexes.Eof do
+ begin
+ IndexMetadata := TableMetadata.IndexMetadatas.Add;
+ IndexMetadata.Name := Trim(Indexes.FieldByName('Key_name').AsString);
+ IndexMetadata.Options := [];
+ if Pos('PRIMARY', Indexes.FieldByName('Key_name').AsString) = 1 then
+ IndexMetadata.Options := IndexMetadata.Options + [ixPrimary, ixUnique]
+ else if Indexes.FieldByName('Non_unique').AsInteger = 0 then
+ IndexMetadata.Options := IndexMetadata.Options + [ixUnique];
+ { TODO : support other Options? e.g partial indexes, descending}
+ IndexFieldList.Clear;
+ KeyName := Indexes.FieldByName('Key_name').AsString;
+ while (not Indexes.Eof) and (KeyName = Indexes.FieldByName('Key_name').AsString) do
+ begin
+ IndexFieldList.Add(Trim(Indexes.FieldByName('Column_name').AsString));
+ Indexes.Next;
+ end;
+ IndexMetadata.Fields := StringReplace(IndexFieldList.CommaText, ',', ';', [rfReplaceAll]);
+ end;
+ finally
+ Indexes.Close;
+ end;
+ finally
+ IndexFieldList.Free;
+ Broker.ReleaseDataSet(Indexes);
+ end;
+end;
+
+procedure TInstantMySQLCatalog.AddFieldMetadatas(TableMetadata: TInstantTableMetadata);
+ function GetFieldSize(AFieldType: string): Integer;
+ var
+ I, J: Integer;
+ begin
+ Result := 0;
+ I := Pos('(', AFieldType);
+ if I > 0 then begin
+ J := PosEx(',', AFieldType, I);
+ if J = 0 then
+ J := Pos(')', AFieldType);
+ Result := StrToIntDef(Copy(AFieldType, I + 1, J -I - 1), 0);
+ end;
+ end;
+
+var
+ Fields: TDataSet;
+ FieldMetadata: TInstantFieldMetadata;
+ AlternateDataTypes: TInstantDataTypes;
+ FieldMetaDataType: TInstantDataType;
+ FieldTypeName: string;
+begin
+ Fields := Broker.AcquireDataSet(GetSelectFieldsSQL(TableMetadata.Name));
+ try
+ Fields.Open;
+ try
+ while not Fields.Eof do
+ begin
+ FieldMetadata := TableMetadata.FieldMetadatas.Add;
+ FieldMetadata.Name := Trim(Fields.FieldByName('Field').AsString);
+ if ColumnTypeToDataType(
+ Fields.FieldByName('Type').AsString,
+ FieldMetaDataType, AlternateDataTypes) then
+ begin
+ FieldMetadata.DataType := FieldMetaDataType;
+ FieldMetadata.AlternateDataTypes := AlternateDataTypes;
+ FieldMetadata.Options := [];
+ if Fields.FieldByName('Null').AsString <> 'YES' then
+ FieldMetadata.Options := FieldMetadata.Options + [foRequired];
+ if TableMetadata.IndexMetadatas.IsFieldIndexed(FieldMetadata) then
+ FieldMetadata.Options := FieldMetadata.Options + [foIndexed];
+ if FieldMetadata.DataType = dtString then
+ FieldMetadata.Size := GetFieldSize(Fields.FieldByName('Type').AsString);
+ end
+ else
+ DoWarning(Format(SUnsupportedColumnSkipped, [
+ TableMetadata.Name, Fields.FieldByName('Field').AsString,
+ Fields.FieldByName('Type').AsString]));
+ Fields.Next;
+ end;
+ finally
+ Fields.Close;
+ end;
+ finally
+ Broker.ReleaseDataSet(Fields);
+ end;
+end;
+
+procedure TInstantMySQLCatalog.AddTableMetadatas(TableMetadatas: TInstantTableMetadatas);
+// NB: unresolved error
+// MySQL and dbx raise a "Commands out of sync" error when using 2 simultaneous datasets
+// store table names in stringlist to work around this
+var
+ Tables: TDataSet;
+ TableMetadata: TInstantTableMetadata;
+ TableList: TStringList;
+ i: Integer;
+begin
+ TableList := TStringList.Create;
+ Tables := Broker.AcquireDataSet(GetSelectTablesSQL());
+ try
+ Tables.Open;
+ try
+ while not Tables.Eof do
+ begin
+ TableList.Add(Trim(Tables.FieldByName('tables_in_' + Broker.Connector.DatabaseName).AsString));
+ Tables.Next;
+ end;
+ finally
+ Tables.Close;
+ end;
+ for i := 0 to TableList.Count - 1 do
+ begin
+ TableMetadata := TableMetadatas.Add;
+ TableMetadata.Name := TableList[i];
+ // Call AddIndexMetadatas first, so that AddFieldMetadatas can see what
+ // indexes are defined to correctly set the foIndexed option.
+ AddIndexMetadatas(TableMetadata);
+ AddFieldMetadatas(TableMetadata);
+ end;
+ finally
+ Broker.ReleaseDataSet(Tables);
+ TableList.Free;
+ end;
+end;
+
+function TInstantMySQLCatalog.ColumnTypeToDataType(const ColumnType: string;
+ out DataType: TInstantDataType; out AlternateDataTypes: TInstantDataTypes):
+ Boolean;
+begin
+// NB: datatypes at http://dev.mysql.com/doc/refman/4.1/en/data-types.html
+ AlternateDataTypes := [];
+ Result := True;
+
+ if Pos('bool', ColumnType) = 1 then
+ DataType := dtBoolean
+ else if Pos('tinyint', ColumnType) = 1 then begin
+ DataType := dtBoolean;
+ Include(AlternateDataTypes, dtInteger);
+ end
+ else if Pos('bigint', ColumnType) = 1 then // prevent "bigint" from matching "int" i.e Int64
+ Result := False
+ else if Pos('int', ColumnType) > 0 then // covers SMALLINT, MEDINT, INT and INTEGER
+ DataType := dtInteger
+ else if Pos('float', ColumnType) = 1 then
+ DataType := dtFloat
+ else if Pos('decimal', ColumnType) = 1 then
+ DataType := dtCurrency
+ else if Pos('char', ColumnType) = 1 then // MySQL stores varchar(3) or smaller as char
+ DataType := dtString
+ else if Pos('varchar', ColumnType) = 1 then
+ DataType := dtString
+ else if Pos('text', ColumnType) > 0 then begin // i.e. text, tinytext, mediumtext or longtext
+ DataType := dtMemo;
+ Include(AlternateDataTypes, dtString); // IO strings longer than 255 stored as MySQL TEXT
+ end
+ else if Pos('blob', ColumnType) > 0 then // blob, tinyblob, mediumblob, longblob
+ DataType := dtBlob
+ else if Pos('datetime', ColumnType) = 1 then
+ DataType := dtDateTime
+ else if Pos('date', ColumnType) = 1 then
+ DataType := dtDate
+ // need the following to prevent "timestamp" from matching with "time"
+ else if Pos('timestamp', ColumnType) = 1 then // seconds since UNIX epoch - not valid for IO
+ Result := False
+ else if Pos('time', ColumnType) = 1 then
+ DataType := dtTime
+ else
+ Result := False;
+end;
+
+function TInstantMySQLCatalog.GetSelectFieldsSQL(
+ const ATableName: string): string;
+begin
+ Result := 'DESCRIBE ' + ATableName; // synonym for MySQL "SHOW COLUMNS FROM"
+end;
+
+function TInstantMySQLCatalog.GetSelectIndexesSQL(const ATableName: string): string;
+begin
+ Result := 'SHOW INDEX FROM ' + ATableName;
+end;
+
+function TInstantMySQLCatalog.GetSelectTablesSQL: string;
+begin
+ Result := 'SHOW TABLES';
+end;
+
+procedure TInstantMySQLCatalog.InitTableMetadatas(ATableMetadatas: TInstantTableMetadatas);
+begin
+ ATableMetadatas.Clear;
+ AddTableMetadatas(ATableMetadatas);
+end;
+
+
+{ A. test table definition
+
+
+CREATE TABLE Test
+(
+ Class VARCHAR(32) NOT NULL,
+ Id VARCHAR(32) NOT NULL,
+ UpdateCount INTEGER,
+ StringAttr VARCHAR(255),
+ StringAttr2 VARCHAR(3), // MySQL stores this as CHAR(3) !!
+ StringAttr3 VARCHAR(256), // MySQL stores this as TEXT
+ BlobAttr BLOB,
+ BooleanAttr SMALLINT,
+ CurrencyAttr DECIMAL(14,4),
+ DateTimeAttr TIMESTAMP,
+ FloatAttr FLOAT,
+ GraphicAttr BLOB,
+ IntegerAttr INTEGER,
+ MemoAttr TEXT,
+ ReferenceAttributeClass VARCHAR(32),
+ ReferenceAttributeId VARCHAR(32),
+ EmbeddedPartsAtttribute BLOB,
+ EmbeddedPartAttribute BLOB,
+ ExternalPartAttributeClass VARCHAR(32),
+ ExternalPartAttributeId VARCHAR(32),
+ EmbeddedReferencesAtttribute BLOB,
+ DateAttr DATE,
+ TimeAttr TIME,
+ PRIMARY KEY (Class, Id)
+);
+
+
+B. Result of "GetSelectFieldsSQL" method - run against Win32 MySQL 4.1.14 with InnoDB engine
+
+Field Type Null Key Default Extra
+===== ==== ==== === ======= =====
+Class varchar(32) PRI
+Id varchar(32) PRI
+UpdateCount int(11) YES
+StringAttr varchar(255) YES
+StringAttr2 char(3) YES
+StringAttr3 text YES
+BlobAttr blob YES
+BooleanAttr smallint(6) YES
+CurrencyAttr decimal(14,4) YES
+DateTimeAttr datetime YES
+FloatAttr double YES
+GraphicAttr blob YES
+IntegerAttr int(11) YES
+MemoAttr text YES
+ReferenceAttributeClass varchar(32) YES
+ReferenceAttributeId varchar(32) YES
+EmbeddedPartsAtttribute blob YES
+EmbeddedPartAttribute blob YES
+ExternalPartAttributeClass varchar(32) YES
+ExternalPartAttributeId varchar(32) YES
+EmbeddedReferencesAtttribute blob YES
+DateAttr date YES
+TimeAttr time YES
+
+}
+
+
+end.
|