|
@@ -0,0 +1,666 @@
|
|
|
+{
|
|
|
+ 单元名: ScAutoUpdateUnit
|
|
|
+ 作者: 张引
|
|
|
+ 时间: 2003/12/29
|
|
|
+ 作用: 升级ACCESS数据库文件
|
|
|
+
|
|
|
+ 限制: 1.只能添加字段,修改字段类型,大小(字符串类型),不能删除字段
|
|
|
+ 2.只支持如下字段:
|
|
|
+ ftString, ftSmallint, ftInteger, ftBoolean, ftSingle, ftDouble,
|
|
|
+ ftCurrency, ftDateTime, ftMemo, ftOLEObject
|
|
|
+
|
|
|
+ 注意: 必须设置TScUpdater.CurrentFileVer,以实现根据版本号判断自动升级。
|
|
|
+}
|
|
|
+unit ScAutoUpdateUnit;
|
|
|
+
|
|
|
+interface
|
|
|
+
|
|
|
+uses
|
|
|
+ SysUtils, DB, ADODB, Classes, ScTablesUnit{, ScFileArchiver, ScFileArchiverConsts};
|
|
|
+
|
|
|
+const
|
|
|
+ MaxFieldCount = 512;
|
|
|
+ PrimaryKey = 'PrimaryKey';
|
|
|
+
|
|
|
+type
|
|
|
+ PFieldDefs = ^TFieldDefs;
|
|
|
+
|
|
|
+ TFieldDefs = array [0..MaxFieldCount - 1] of TScFieldDef;
|
|
|
+
|
|
|
+ PTableDef = ^TTableDef;
|
|
|
+
|
|
|
+ // 表定义结构
|
|
|
+ TTableDef = record
|
|
|
+ // 表名
|
|
|
+ TableName: string;
|
|
|
+ // 字段数
|
|
|
+ FieldCount: Integer;
|
|
|
+ // 字段结构数组
|
|
|
+ FieldDefs: PFieldDefs;
|
|
|
+ // 是否需要重新创建
|
|
|
+ Recreate: Boolean;
|
|
|
+ // 重新创建主键
|
|
|
+ RecreatePrimaryKey: Boolean;
|
|
|
+ end;
|
|
|
+
|
|
|
+ TSQLType = (stAlter, stCreate, stReCreate);
|
|
|
+
|
|
|
+ TUpdateEventType = (uetAddFields, uetKeys, uetAfterUpdate);
|
|
|
+
|
|
|
+ TUpdateEvent = procedure (ATableName: string; AEventType: TUpdateEventType;
|
|
|
+ ASQLType: TSQLType; AConnection: TADOConnection);
|
|
|
+ TUpdatedEvent = procedure (AFileVersion, ACurVersion: string;
|
|
|
+ AConnection: TADOConnection; ASucceed: Boolean);
|
|
|
+
|
|
|
+ EScUpdater = class(Exception);
|
|
|
+
|
|
|
+ TScUpdater = class(TObject)
|
|
|
+ private
|
|
|
+ FTableDefList: TList;
|
|
|
+ FFileName: string;
|
|
|
+ FConnection: TADOConnection;
|
|
|
+ FFileVer: string;
|
|
|
+ FQuery: TADOQuery;
|
|
|
+ FForceUpdate: Boolean;
|
|
|
+ FForceCheck: Boolean;
|
|
|
+ FCurFileVersion: string;
|
|
|
+ FOnUpdateData: TUpdateEvent;
|
|
|
+ FOnUpdated: TUpdatedEvent;
|
|
|
+ function GetCurFileVersion: string;
|
|
|
+ // 返回True:表存在,返回False: 表不存在
|
|
|
+ function CheckTable(ATableName: string): Boolean;
|
|
|
+ procedure GenerateSQL(ATableDef: PTableDef; ASQLType: TSQLType; ASQLList: TStrings);
|
|
|
+ procedure InternalExcuteSQL(ASQL: string; AHideException: Boolean = False; AOpen: Boolean = False);
|
|
|
+ function ExcuteUpdateSQL(ASQLList: TStrings): Boolean;
|
|
|
+ procedure SetForceUpdate(const Value: Boolean);
|
|
|
+ procedure SetForceCheck(const Value: Boolean);
|
|
|
+ // 字符串是否是事件
|
|
|
+ // 事件字符串格式:"事件名 表名 操作类型"
|
|
|
+ function CheckEvent(AText: string): Boolean;
|
|
|
+ public
|
|
|
+ constructor Create;
|
|
|
+ destructor Destroy; override;
|
|
|
+ // 打开(AFileVers:打开文件的版本,程序中最新的文件版本号)
|
|
|
+ procedure Open(AFileName: string; AConnection: TADOConnection;
|
|
|
+ AFileVer, ACurrentFileVer: string);
|
|
|
+ // 关闭
|
|
|
+ procedure Close;
|
|
|
+ // 执行升级操作
|
|
|
+ function ExcuteUpdate: Boolean;
|
|
|
+ // 执行其他SQL语句(如建立、删除索引)
|
|
|
+ procedure ExcuteSQL(ASQL: string);
|
|
|
+ // 添加表定义
|
|
|
+ function AddTableDef(ATableName: string; AFieldDefs: PFieldDefs; AFieldCount: Integer;
|
|
|
+ AReCreate, ARecreatePK: Boolean): Integer;
|
|
|
+ // 文件是否需要升级(根据版本号判断)
|
|
|
+ function FileNeedUpdate: Boolean;
|
|
|
+ // 强制进行表和字段的升级检查
|
|
|
+ property ForceCheck: Boolean read FForceCheck write SetForceCheck;
|
|
|
+ // 强制升级所有表和字段
|
|
|
+ property ForceUpdate: Boolean read FForceUpdate write SetForceUpdate;
|
|
|
+ // 最新文件版本
|
|
|
+ property CurrentFileVer: string read GetCurFileVersion;
|
|
|
+ // 事件
|
|
|
+ // 重建关键字之前
|
|
|
+ property OnUpdateData: TUpdateEvent read FOnUpdateData
|
|
|
+ write FOnUpdateData;
|
|
|
+ // 完成升级后
|
|
|
+ property OnUpdated: TUpdatedEvent read FOnUpdated
|
|
|
+ write FOnUpdated;
|
|
|
+ end;
|
|
|
+
|
|
|
+const
|
|
|
+ SQLTypeStrs: array [TSQLType] of string = ('Modify', 'Create', 'ReCreate');
|
|
|
+ SOnUpdateData = 'OnUpdateData';
|
|
|
+
|
|
|
+implementation
|
|
|
+
|
|
|
+uses
|
|
|
+ ScUtils;
|
|
|
+
|
|
|
+function StrToSQLType(ASQLType: string): TSQLType;
|
|
|
+var
|
|
|
+ I: TSQLType;
|
|
|
+begin
|
|
|
+ Result := stAlter;
|
|
|
+ for I := Low(SQLTypeStrs) to High(SQLTypeStrs) do
|
|
|
+ begin
|
|
|
+ if SameText(ASQLType, SQLTypeStrs[I]) then
|
|
|
+ begin
|
|
|
+ Result := I;
|
|
|
+ Break;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+{ TScUpdater }
|
|
|
+
|
|
|
+function TScUpdater.AddTableDef(ATableName: string; AFieldDefs: PFieldDefs;
|
|
|
+ AFieldCount: Integer; AReCreate, ARecreatePK: Boolean): Integer;
|
|
|
+var
|
|
|
+ pRec: PTableDef;
|
|
|
+begin
|
|
|
+ New(pRec);
|
|
|
+ pRec^.TableName := ATableName;
|
|
|
+ pRec^.FieldCount := AFieldCount;
|
|
|
+ pRec^.FieldDefs := AFieldDefs;
|
|
|
+ pRec^.Recreate := AReCreate;
|
|
|
+ pRec^.RecreatePrimaryKey := ARecreatePK;
|
|
|
+ Result := FTableDefList.Add(pRec);
|
|
|
+end;
|
|
|
+
|
|
|
+function TScUpdater.CheckEvent(AText: string): Boolean;
|
|
|
+var
|
|
|
+ strText, strEvent, strTableName, strEventType, strSQLType: string;
|
|
|
+ SQLType: TSQLType;
|
|
|
+ iPos: Integer;
|
|
|
+begin
|
|
|
+ Result := False;
|
|
|
+ strText := AText;
|
|
|
+ // 检查事件名称
|
|
|
+ iPos := Pos(' ', strText);
|
|
|
+ if iPos > 0 then
|
|
|
+ begin
|
|
|
+ strEvent := Copy(strText, 1, iPos - 1);
|
|
|
+ Delete(strText, 1, iPos);
|
|
|
+ if SameText(strEvent, SOnUpdateData) then
|
|
|
+ begin
|
|
|
+ // 事件类型
|
|
|
+ iPos := Pos(' ', strText);
|
|
|
+ strEventType := Copy(strText, 1, iPos - 1);
|
|
|
+ Delete(strText, 1, iPos);
|
|
|
+ // 表名
|
|
|
+ iPos := Pos(' ', strText);
|
|
|
+ strTableName := Copy(strText, 1, iPos - 1);
|
|
|
+ Delete(strText, 1, iPos);
|
|
|
+ // 操作类型
|
|
|
+ strSQLType := strText;
|
|
|
+ SQLType := StrToSQLType(strSQLType);
|
|
|
+
|
|
|
+ Result := True;
|
|
|
+ if Assigned(FOnUpdateData) then
|
|
|
+ FOnUpdateData(strTableName, TUpdateEventType(StrToInt(strEventType)), SQLType, FConnection);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+function TScUpdater.CheckTable(ATableName: string): Boolean;
|
|
|
+var
|
|
|
+ I: Integer;
|
|
|
+ Names: TStringList;
|
|
|
+begin
|
|
|
+ Names := TStringList.Create;
|
|
|
+ try
|
|
|
+ FConnection.GetTableNames(Names);
|
|
|
+ if Names.IndexOf(ATableName) < 0 then
|
|
|
+ Result := False
|
|
|
+ else
|
|
|
+ Result := True;
|
|
|
+ finally
|
|
|
+ Names.Free;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TScUpdater.Close;
|
|
|
+begin
|
|
|
+ FQuery.Close;
|
|
|
+end;
|
|
|
+
|
|
|
+constructor TScUpdater.Create;
|
|
|
+begin
|
|
|
+ FForceUpdate := False;
|
|
|
+ FForceCheck := False;
|
|
|
+ FTableDefList := TList.Create;
|
|
|
+ FQuery := TADOQuery.Create(nil);
|
|
|
+end;
|
|
|
+
|
|
|
+destructor TScUpdater.Destroy;
|
|
|
+begin
|
|
|
+ Close;
|
|
|
+ FQuery.Free;
|
|
|
+ ClearPointerList(FTableDefList);
|
|
|
+ FTableDefList.Free;
|
|
|
+ inherited;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TScUpdater.ExcuteSQL(ASQL: string);
|
|
|
+begin
|
|
|
+ InternalExcuteSQL(ASQL);
|
|
|
+end;
|
|
|
+
|
|
|
+function TScUpdater.ExcuteUpdate: Boolean;
|
|
|
+var
|
|
|
+ I: Integer;
|
|
|
+ pRec: PTableDef;
|
|
|
+ SQLs: TStringList;
|
|
|
+ SQLType: TSQLType;
|
|
|
+ bHasError: Boolean;
|
|
|
+ sError: string;
|
|
|
+begin
|
|
|
+ Result := False;
|
|
|
+ bHasError := False;
|
|
|
+ sError := '';
|
|
|
+ if FileNeedUpdate then
|
|
|
+ begin
|
|
|
+ SQLs := TStringList.Create;
|
|
|
+ try
|
|
|
+ for I := 0 to FTableDefList.Count - 1 do
|
|
|
+ begin
|
|
|
+ pRec := PTableDef(FTableDefList[I]);
|
|
|
+ if CheckTable(pRec^.TableName) then
|
|
|
+ begin
|
|
|
+ if pRec^.Recreate then
|
|
|
+ SQLType := stReCreate
|
|
|
+ else
|
|
|
+ SQLType := stAlter;
|
|
|
+ end
|
|
|
+ else
|
|
|
+ SQLType := stCreate;
|
|
|
+ GenerateSQL(pRec, SQLType, SQLs);
|
|
|
+ if SQLs.Count > 0 then
|
|
|
+ if not ExcuteUpdateSQL(SQLs) then
|
|
|
+ begin
|
|
|
+ bHasError := True;
|
|
|
+ sError := sError + #13#10 + Format('Update operation [%s] on table [%s] can not be executed!', [SQLTypeStrs[SQLType], pRec^.TableName]);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ finally
|
|
|
+ SQLs.Free;
|
|
|
+ end;
|
|
|
+
|
|
|
+ if bHasError then
|
|
|
+ MessageWarning(0, '升级文件时发生错误,无法完成升级。'#13#10'错误信息:' + sError)
|
|
|
+ else
|
|
|
+ Result := True;
|
|
|
+
|
|
|
+ if Assigned(FOnUpdated) then
|
|
|
+ FOnUpdated(FFileVer, CurrentFileVer, FConnection, Result);
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+function TScUpdater.ExcuteUpdateSQL(ASQLList: TStrings): Boolean;
|
|
|
+var
|
|
|
+ I: Integer;
|
|
|
+ HideExcption: Boolean;
|
|
|
+begin
|
|
|
+ Result := False;
|
|
|
+ try
|
|
|
+ for I := 0 to ASQLList.Count - 1 do
|
|
|
+ begin
|
|
|
+ if not CheckEvent(ASQLList[I]) then
|
|
|
+ begin
|
|
|
+ HideExcption := ASQLList.Objects[I] <> nil;
|
|
|
+ if HideExcption then
|
|
|
+ HideExcption := Boolean(Integer(ASQLList.Objects[I]));
|
|
|
+ InternalExcuteSQL(ASQLList[I], HideExcption);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ Result := True;
|
|
|
+ except
|
|
|
+
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+function TScUpdater.FileNeedUpdate: Boolean;
|
|
|
+begin
|
|
|
+ if GetCurFileVersion = '' then
|
|
|
+ raise EScUpdater.Create('必须设置TScUpdater.CurrentFileVer!');
|
|
|
+ Result := (ScCompareFileVer(FFileVer, GetCurFileVersion) < 0) or FForceCheck;
|
|
|
+end;
|
|
|
+
|
|
|
+function SameFieldType(AFieldType: TFieldType; AScFieldType: TScMDBFieldType): Boolean;
|
|
|
+begin
|
|
|
+ Result := False;
|
|
|
+ case AScFieldType of
|
|
|
+ ftString:
|
|
|
+ Result := (AFieldType = DB.ftWideString) or (AFieldType = DB.ftString);
|
|
|
+ ftByte:
|
|
|
+ Result := AFieldType = DB.ftWord;
|
|
|
+ ftSmallint:
|
|
|
+ Result := AFieldType = DB.ftSmallint;
|
|
|
+ ftInteger:
|
|
|
+ Result := AFieldType = DB.ftInteger;
|
|
|
+ ftBoolean:
|
|
|
+ Result := AFieldType = DB.ftBoolean;
|
|
|
+ ftSingle:
|
|
|
+ Result := AFieldType = DB.ftFloat;
|
|
|
+ ftDouble:
|
|
|
+ Result := AFieldType = DB.ftFloat;
|
|
|
+ ftCurrency:
|
|
|
+ Result := (AFieldType = DB.ftCurrency) or (AFieldType = DB.ftBCD);
|
|
|
+ ftDateTime:
|
|
|
+ Result := AFieldType = DB.ftDateTime;
|
|
|
+ ftMemo:
|
|
|
+ Result := AFieldType = DB.ftMemo;
|
|
|
+ ftOLEObject:
|
|
|
+ Result := AFieldType = DB.ftBlob;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TScUpdater.GenerateSQL(ATableDef: PTableDef; ASQLType: TSQLType; ASQLList: TStrings);
|
|
|
+var
|
|
|
+ bHasKey: Boolean;
|
|
|
+
|
|
|
+ function GenerateCreateSQL: string;
|
|
|
+ var
|
|
|
+ I: Integer;
|
|
|
+ Def: TScFieldDef;
|
|
|
+ strField, strFields, strKeyFields: string;
|
|
|
+ begin
|
|
|
+ Result := '';
|
|
|
+ if ATableDef^.FieldCount > 0 then
|
|
|
+ begin
|
|
|
+ // CREATE TABLE table1
|
|
|
+ Result := Format('CREATE TABLE %s ', [ATableDef^.TableName]);
|
|
|
+ strFields := '';
|
|
|
+ strKeyFields := '';
|
|
|
+ for I := 0 to ATableDef^.FieldCount - 1 do
|
|
|
+ begin
|
|
|
+ Def := ATableDef^.FieldDefs[I];
|
|
|
+ // field1 type
|
|
|
+ strField := Def.FieldName + ' ' + ScMDBFieldTypeName[Def.FieldType];
|
|
|
+
|
|
|
+ case Def.FieldType of
|
|
|
+ ftString:
|
|
|
+ // field1 type (size)
|
|
|
+ strField := strField + Format(' (%d)', [Def.Size]);
|
|
|
+ ftFMTBCD:
|
|
|
+ // field1 type (size, Precision)
|
|
|
+ strField := strField + Format(' (%d,%d)', [Def.Size, Def.Precision]);
|
|
|
+ end;
|
|
|
+ if Def.NotNull then
|
|
|
+ // field1 type (size) NOT NULL
|
|
|
+ strField := strField + ' ' + 'NOT NULL';
|
|
|
+ if Def.PrimaryKey then
|
|
|
+ strKeyFields := strKeyFields + Def.FieldName + ', ';
|
|
|
+
|
|
|
+ strFields := strFields + strField + ', ';
|
|
|
+ end;
|
|
|
+ if strKeyFields <> '' then
|
|
|
+ begin
|
|
|
+ Delete(strKeyFields, Length(strKeyFields) - 1, 2);
|
|
|
+ // CONSTRAINT PrimaryKey PRIMARY KEY (field1, field2...)
|
|
|
+ strKeyFields := Format('CONSTRAINT %s PRIMARY KEY (%s)', [PrimaryKey, strKeyFields]);
|
|
|
+ end
|
|
|
+ else
|
|
|
+ Delete(strFields, Length(strFields) - 1, 2);
|
|
|
+ // (field1 type (size) NOT NULL, field2 type (size) NOT NULL..., CONSTRAINT PrimaryKey PRIMARY KEY (field1, field2...))
|
|
|
+ strFields := Format('(%s)', [strFields + strKeyFields]);
|
|
|
+ // CREATE TABLE table1 (field1 type (size) NOT NULL, field2 type (size) NOT NULL..., CONSTRAINT PrimaryKey PRIMARY KEY (field1, field2...))
|
|
|
+ Result := Result + strFields;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+type
|
|
|
+ TAlterType = (atAddField, atAlterField, atDropField, atAddIndex, atDropIndex);
|
|
|
+
|
|
|
+ function GenerateSingleFieldAlterSQL(ATableName: string; AFieldDef: TScFieldDef;
|
|
|
+ AOp: TAlterType; var ANeedDefault: Boolean): string;
|
|
|
+ begin
|
|
|
+ Result := '';
|
|
|
+ ANeedDefault := False;
|
|
|
+ case AOp of
|
|
|
+ atAddField:
|
|
|
+ begin
|
|
|
+ Result := Format('ALTER TABLE %s ADD COLUMN %s %s',
|
|
|
+ [ATableName, AFieldDef.FieldName, ScMDBFieldTypeName[AFieldDef.FieldType]]);
|
|
|
+ case AFieldDef.FieldType of
|
|
|
+ ftString:
|
|
|
+ Result := Result + Format(' (%d)', [AFieldDef.Size]);
|
|
|
+ ftFMTBCD:
|
|
|
+ Result := Result + Format(' (%d,%d)', [AFieldDef.Size, AFieldDef.Precision]);
|
|
|
+ end;
|
|
|
+ if AFieldDef.NotNull then
|
|
|
+ begin
|
|
|
+ Result := Result + ' NOT NULL';
|
|
|
+ case AFieldDef.FieldType of
|
|
|
+ ftByte, ftSmallint, ftInteger, ftBoolean, ftSingle, ftDouble,
|
|
|
+ ftCurrency, ftDateTime, ftFMTBCD:
|
|
|
+ ANeedDefault := True;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ atDropField:
|
|
|
+ begin
|
|
|
+ Result := Format('ALTER TABLE %s DROP COLUMN %s', [ATableName, AFieldDef.FieldName]);
|
|
|
+ end;
|
|
|
+ atAlterField:
|
|
|
+ begin
|
|
|
+ Result := Format('ALTER TABLE %s ALTER COLUMN %s %s',
|
|
|
+ [ATableName, AFieldDef.FieldName, ScMDBFieldTypeName[AFieldDef.FieldType]]);
|
|
|
+ case AFieldDef.FieldType of
|
|
|
+ ftString:
|
|
|
+ Result := Result + Format(' (%d)', [AFieldDef.Size]);
|
|
|
+ ftFMTBCD:
|
|
|
+ Result := Result + Format(' (%d,%d)', [AFieldDef.Size, AFieldDef.Precision]);
|
|
|
+ end;
|
|
|
+ if AFieldDef.NotNull then
|
|
|
+ begin
|
|
|
+ Result := Result + ' NOT NULL';
|
|
|
+ case AFieldDef.FieldType of
|
|
|
+ ftByte, ftSmallint, ftInteger, ftBoolean, ftSingle, ftDouble,
|
|
|
+ ftCurrency, ftDateTime, ftFMTBCD:
|
|
|
+ ANeedDefault := True;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+ function GenerateDefaultValueSQL(ATableName: string; AFieldDef: TScFieldDef): string;
|
|
|
+ begin
|
|
|
+ Result := '';
|
|
|
+ case AFieldDef.FieldType of
|
|
|
+ ftByte, ftSmallint, ftInteger:
|
|
|
+ Result := Format('UPDATE %s SET %s = %d WHERE IsNull(%s)',
|
|
|
+ [ATableName, AFieldDef.FieldName, 0, AFieldDef.FieldName]);
|
|
|
+ ftSingle, ftDouble, ftCurrency, ftFMTBCD:
|
|
|
+ Result := Format('UPDATE %s SET %s = %f WHERE IsNull(%s)',
|
|
|
+ [ATableName, AFieldDef.FieldName, 0.0, AFieldDef.FieldName]);
|
|
|
+ ftBoolean:
|
|
|
+ Result := Format('UPDATE %s SET %s = %s WHERE IsNull(%s)',
|
|
|
+ [ATableName, AFieldDef.FieldName, 'FALSE', AFieldDef.FieldName]);
|
|
|
+ ftDateTime:
|
|
|
+ Result := Format('UPDATE %s SET %s = ''%s'' WHERE IsNull(%s)',
|
|
|
+ [ATableName, AFieldDef.FieldName, '2000-1-1 12:00:00', AFieldDef.FieldName]);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+ function GenerateSingleKeyAlterSQL(ATableName, AIndexName, AFieldNames: string; AOp: TAlterType): string;
|
|
|
+ begin
|
|
|
+ Result := '';
|
|
|
+ case AOp of
|
|
|
+ atAddIndex:
|
|
|
+ begin
|
|
|
+ Result := Format('ALTER TABLE %s ADD CONSTRAINT %s Primary Key (%s)', [ATableName, AIndexName, AFieldNames]);
|
|
|
+ end;
|
|
|
+ atDropIndex:
|
|
|
+ begin
|
|
|
+ Result := Format('ALTER TABLE %s DROP CONSTRAINT %s', [ATableName, AIndexName]);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+ procedure GenerateAlterSQL;
|
|
|
+ var
|
|
|
+ I, J: Integer;
|
|
|
+ Field: TField;
|
|
|
+ pDef: PScFieldDef;
|
|
|
+ AddList, ModifyList: TList;
|
|
|
+ KeyFields: string;
|
|
|
+ bNeedDefaultValue: Boolean;
|
|
|
+ begin
|
|
|
+ InternalExcuteSQL(Format('SELECT * FROM %s WHERE 0=1', [ATableDef^.TableName]), False, True);
|
|
|
+
|
|
|
+ AddList := TList.Create;
|
|
|
+ ModifyList := TList.Create;
|
|
|
+ try
|
|
|
+ KeyFields := '';
|
|
|
+ for I := 0 to ATableDef^.FieldCount - 1 do
|
|
|
+ begin
|
|
|
+ pDef := @ATableDef^.FieldDefs^[I];
|
|
|
+ if (KeyFields <> '') and pDef^.PrimaryKey then
|
|
|
+ KeyFields := KeyFields + ', ';
|
|
|
+ if pDef^.PrimaryKey then
|
|
|
+ KeyFields := KeyFields + pDef^.FieldName;
|
|
|
+ AddList.Add(pDef);
|
|
|
+ end;
|
|
|
+
|
|
|
+{ if KeyFields <> '' then
|
|
|
+ Delete(KeyFields, Length(KeyFields) - 1, 2);}
|
|
|
+
|
|
|
+ for I := 0 to FQuery.Fields.Count - 1 do
|
|
|
+ begin
|
|
|
+ Field := FQuery.Fields[I];
|
|
|
+ for J := 0 to AddList.Count - 1 do
|
|
|
+ begin
|
|
|
+ pDef := PScFieldDef(AddList[J]);
|
|
|
+ if SameText(Field.FieldName, pDef^.FieldName) then
|
|
|
+ begin
|
|
|
+ if FForceUpdate or pDef^.ForceUpdate then
|
|
|
+ ModifyList.Add(pDef)
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ if not SameFieldType(Field.DataType, pDef^.FieldType) then
|
|
|
+ ModifyList.Add(pDef)
|
|
|
+ else if (Field.DataType in [ftWideString]) and (Field.Size <> pDef^.Size) then
|
|
|
+ ModifyList.Add(pDef)
|
|
|
+ // ADO读不到数据库中字段的Required,所以这句代码无用
|
|
|
+ (*
|
|
|
+ else if Field.Required <> pDef^.NotNull then
|
|
|
+ ModifyList.Add(pDef)*);
|
|
|
+ end;
|
|
|
+ AddList.Remove(pDef);
|
|
|
+ Break;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+ for I := 0 to ModifyList.Count - 1 do
|
|
|
+ begin
|
|
|
+ pDef := PScFieldDef(ModifyList[I]);
|
|
|
+ ASQLList.Add(GenerateSingleFieldAlterSQL(ATableDef^.TableName, pDef^, atAlterField, bNeedDefaultValue));
|
|
|
+ if bNeedDefaultValue then
|
|
|
+ ASQLList.Add(GenerateDefaultValueSQL(ATableDef^.TableName, pDef^));
|
|
|
+ end;
|
|
|
+
|
|
|
+ for I := 0 to AddList.Count - 1 do
|
|
|
+ begin
|
|
|
+ pDef := PScFieldDef(AddList[I]);
|
|
|
+ ASQLList.Add(GenerateSingleFieldAlterSQL(ATableDef^.TableName, pDef^, atAddField, bNeedDefaultValue));
|
|
|
+ if bNeedDefaultValue then
|
|
|
+ ASQLList.Add(GenerateDefaultValueSQL(ATableDef^.TableName, pDef^));
|
|
|
+ end;
|
|
|
+ if AddList.Count > 0 then
|
|
|
+ // 添加事件
|
|
|
+ ASQLList.Add(Format('%s %d %s %s', [SOnUpdateData, Ord(uetAddFields), ATableDef.TableName, SQLTypeStrs[ASQLType]]));
|
|
|
+
|
|
|
+
|
|
|
+ if ATableDef.RecreatePrimaryKey then
|
|
|
+ begin
|
|
|
+ if bHasKey then
|
|
|
+ ASQLList.AddObject(GenerateSingleKeyAlterSQL(ATableDef^.TableName, PrimaryKey, KeyFields, atDropIndex), TObject(Integer(True)));
|
|
|
+ // 添加事件
|
|
|
+ ASQLList.Add(Format('%s %d %s %s', [SOnUpdateData, Ord(uetKeys), ATableDef.TableName, SQLTypeStrs[ASQLType]]));
|
|
|
+ ASQLList.Add(GenerateSingleKeyAlterSQL(ATableDef^.TableName, PrimaryKey, KeyFields, atAddIndex));
|
|
|
+ end;
|
|
|
+ finally
|
|
|
+ AddList.Free;
|
|
|
+ ModifyList.Free;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+ procedure CheckHasKey;
|
|
|
+ var
|
|
|
+ Table: TADOTable;
|
|
|
+ I: Integer;
|
|
|
+ begin
|
|
|
+ bHasKey := False;
|
|
|
+ Table := TADOTable.Create(nil);
|
|
|
+ try
|
|
|
+ Table.Connection := FConnection;
|
|
|
+ Table.TableName := ATableDef^.TableName;
|
|
|
+ Table.Open;
|
|
|
+ for I := 0 to Table.FieldCount - 1 do
|
|
|
+ if pfInKey in Table.Fields[I].ProviderFlags then
|
|
|
+ begin
|
|
|
+ bHasKey := True;
|
|
|
+ Break;
|
|
|
+ end;
|
|
|
+ finally
|
|
|
+ Table.Free;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+begin
|
|
|
+ ASQLList.Clear;
|
|
|
+ bHasKey := True;
|
|
|
+ case ASQLType of
|
|
|
+ stAlter:
|
|
|
+ begin
|
|
|
+ // zhangyin 2014-01-26虽然此方法可以避免调试时弹出异常,但是会占用比较长的时间
|
|
|
+ // (4500行清单的项目会将升级数据库时间加长一倍以上 1s->2.4s)
|
|
|
+ // 所以还是屏蔽掉。
|
|
|
+ //CheckHasKey;
|
|
|
+ GenerateAlterSQL;
|
|
|
+ end;
|
|
|
+ stCreate:
|
|
|
+ ASQLList.Add(GenerateCreateSQL);
|
|
|
+ stReCreate:
|
|
|
+ begin
|
|
|
+ ASQLList.Add(Format('DROP TABLE %s', [ATableDef^.TableName]));
|
|
|
+ ASQLList.Add(GenerateCreateSQL);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ if ASQLList.Count > 0 then
|
|
|
+ ASQLList.Add(Format('%s %d %s %s', [SOnUpdateData, Ord(uetAfterUpdate), ATableDef^.TableName, SQLTypeStrs[ASQLType]]));
|
|
|
+end;
|
|
|
+
|
|
|
+function TScUpdater.GetCurFileVersion: string;
|
|
|
+begin
|
|
|
+ Result := FCurFileVersion;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TScUpdater.InternalExcuteSQL(ASQL: string; AHideException, AOpen: Boolean);
|
|
|
+begin
|
|
|
+ FQuery.Close;
|
|
|
+ FQuery.SQL.Clear;
|
|
|
+ FQuery.SQL.Add(ASQL);
|
|
|
+ try
|
|
|
+ if AOpen then
|
|
|
+ FQuery.Open
|
|
|
+ else
|
|
|
+ FQuery.ExecSQL;
|
|
|
+ except
|
|
|
+ if not AHideException then
|
|
|
+ raise;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TScUpdater.Open(AFileName: string; AConnection: TADOConnection;
|
|
|
+ AFileVer, ACurrentFileVer: string);
|
|
|
+begin
|
|
|
+ FFileName := AFileName;
|
|
|
+ FConnection := AConnection;
|
|
|
+ FFileVer := AFileVer;
|
|
|
+ if AFileVer = '' then
|
|
|
+ FFileVer := '0.0.0.0';
|
|
|
+ FCurFileVersion := ACurrentFileVer;
|
|
|
+ // 为空时,设一个很大的当前版本号,强制升级
|
|
|
+ if FCurFileVersion = '' then
|
|
|
+ FCurFileVersion := '100000.0.0.0';
|
|
|
+ FQuery.Connection := AConnection;
|
|
|
+ ClearPointerList(FTableDefList);
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TScUpdater.SetForceCheck(const Value: Boolean);
|
|
|
+begin
|
|
|
+ FForceCheck := Value;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TScUpdater.SetForceUpdate(const Value: Boolean);
|
|
|
+begin
|
|
|
+ FForceUpdate := Value;
|
|
|
+end;
|
|
|
+
|
|
|
+end.
|