1 |
unit UdrSelectInto; |
2 |
|
3 |
{ |
4 |
create or alter procedure select_into ( |
5 |
select_statement blob sub_type 1 not null, |
6 |
table_name varchar(63) not null, |
7 |
table_type varchar(25) not null -- 'global temporary preserve', 'global temporary[ delete]', <empty string> for standard table |
8 |
) returns ( |
9 |
status varchar(100) |
10 |
) |
11 |
external name 'selectinto!select_into' |
12 |
engine udr; |
13 |
} |
14 |
|
15 |
interface |
16 |
|
17 |
uses Classes, SysUtils, IB, FBUDRController, FBUDRIntf; |
18 |
|
19 |
type |
20 |
TSelectInto = class(TFBUDRExecuteProcedure) |
21 |
public |
22 |
procedure Execute(context: IFBUDRExternalContext; |
23 |
ProcMetadata: IFBUDRProcMetadata; |
24 |
InputParams: IFBUDRInputParams; |
25 |
OutputData: IFBUDROutputData); override; |
26 |
end; |
27 |
|
28 |
|
29 |
implementation |
30 |
|
31 |
uses StrUtils, IBUtils; |
32 |
|
33 |
function SQLType2Name(SQLType: Cardinal) : AnsiString; |
34 |
begin |
35 |
case SQLType of |
36 |
SQL_VARYING: Result := 'VARCHAR'; |
37 |
SQL_TEXT: Result := 'CHAR'; |
38 |
SQL_DOUBLE: Result := 'DOUBLE PRECISION'; |
39 |
SQL_FLOAT: Result := 'FLOAT'; |
40 |
SQL_LONG: Result := 'INTEGER'; |
41 |
SQL_SHORT: Result := 'SMALLINT'; |
42 |
SQL_TIMESTAMP: Result := 'TIMESTAMP'; |
43 |
SQL_TIMESTAMP_TZ: Result := 'TIMESTAMP WITH TIMEZONE'; |
44 |
SQL_TIMESTAMP_TZ_EX: Result := 'TIMESTAMP WITH TIMEZONE'; |
45 |
SQL_BLOB: Result := 'BLOB'; |
46 |
SQL_D_FLOAT: Result := 'FLOAT'; |
47 |
SQL_TYPE_TIME: Result := 'TIME'; |
48 |
SQL_TYPE_DATE: Result := 'DATE'; |
49 |
SQL_INT64: Result := 'BIGINT'; |
50 |
SQL_TIME_TZ: Result := 'TIME WITH TIMEZONE'; |
51 |
SQL_TIME_TZ_EX: Result := 'TIME WITH TIMEZONE'; |
52 |
SQL_DEC16: Result := 'DECFLOAT(16)'; |
53 |
SQL_DEC34: Result := 'DECFLOAT(34)'; |
54 |
SQL_INT128: Result := 'INT128'; |
55 |
SQL_NULL: Result := 'VARCHAR(1)'; |
56 |
SQL_BOOLEAN: Result := 'BOOLEAN'; |
57 |
else |
58 |
Result := 'UNKNOWN'; |
59 |
end |
60 |
end; |
61 |
|
62 |
function Fld2SQLTypeDef(SQLType: Cardinal; SubType: Integer; Size: Cardinal; Scale: Integer; CharSetClause: String) : AnsiString; |
63 |
var |
64 |
TypeDef: AnsiString; |
65 |
begin |
66 |
TypeDef := SQLType2Name(SQLType); |
67 |
|
68 |
if Scale < 0 then |
69 |
case SQLType of |
70 |
SQL_SHORT: TypeDef := format('NUMERIC(4,%d)', [-Scale]); |
71 |
SQL_LONG: TypeDef := format('NUMERIC(9,%d)', [-Scale]); |
72 |
SQL_DOUBLE: TypeDef := format('NUMERIC(15,%d)', [-Scale]); |
73 |
SQL_INT64: TypeDef := format('NUMERIC(18,%d)', [-Scale]); |
74 |
SQL_INT128: TypeDef := format('NUMERIC(35,%d)', [-Scale]); |
75 |
end |
76 |
else |
77 |
case SQLType of |
78 |
SQL_VARYING, |
79 |
SQL_TEXT: TypeDef := format(TypeDef + '(%d) %s', [Size, CharSetClause]); |
80 |
SQL_BLOB: TypeDef := format(TypeDef + ' SUB_TYPE %d %s', [SubType, CharSetClause]); |
81 |
end; |
82 |
Result := TypeDef; |
83 |
end; |
84 |
|
85 |
procedure TSelectInto.Execute(context: IFBUDRExternalContext; ProcMetadata: IFBUDRProcMetadata; InputParams: IFBUDRInputParams; OutputData: IFBUDROutputData); |
86 |
var |
87 |
Statement: IStatement; |
88 |
TX2: ITransaction; |
89 |
Dialect, DefaultCharSetID, i: Integer; |
90 |
SelectSQL, TableName, TableType: AnsiString; |
91 |
TypeDef, TypeDefs, CharSetClause, Command, CommitAction, SQL: AnsiString; |
92 |
begin |
93 |
TypeDefs := ''; |
94 |
with context do |
95 |
begin |
96 |
// prepare and analyse select_statement and create DDL of target table columns |
97 |
SelectSQL := InputParams.ByName('select_statement').AsString; |
98 |
Dialect := GetAttachment.GetSQLDialect; |
99 |
DefaultCharSetID := context.GetAttachment.getCharSetID; |
100 |
CharSetClause := ''; |
101 |
|
102 |
Statement := GetAttachment.Prepare(GetTransaction, SelectSQL); |
103 |
with Statement.MetaData do |
104 |
begin |
105 |
for i := 0 to Count -1 do |
106 |
begin |
107 |
with ColMetaData[i] do |
108 |
begin |
109 |
case GetSQLType of SQL_VARYING, |
110 |
SQL_TEXT, |
111 |
SQL_BLOB: if (DefaultCharSetID <> getCharSetID) then |
112 |
CharSetClause := 'CHARACTER SET ' + GetAttachment.GetCharsetName(getCharSetID); |
113 |
end; |
114 |
TypeDef := Fld2SQLTypeDef(GetSQLType, SQLSubtype, Size, Scale, CharSetClause); |
115 |
TypeDefs := TypeDefs + ','#13#10 + QuoteIdentifierIfNeeded(Dialect, Name) + ' ' + TypeDef; |
116 |
end; |
117 |
end; |
118 |
Delete(TypeDefs, 1, 1); |
119 |
end; |
120 |
|
121 |
// create target table DDL |
122 |
TableName := InputParams.ByName('table_name').AsString; |
123 |
TableType := UpperCase(InputParams.ByName('table_type').AsString); |
124 |
|
125 |
// default: recreate standard table |
126 |
Command := 'RECREATE '; |
127 |
|
128 |
// global temporary? |
129 |
CommitAction := ''; |
130 |
if AnsiContainsStr(TableType, 'GLOBAL TEMPORARY') then |
131 |
begin |
132 |
Command := Command + 'GLOBAL TEMPORARY'; |
133 |
if AnsiContainsStr(TableType, 'PRESERVE') then |
134 |
CommitAction := 'ON COMMIT PRESERVE ROWS'; |
135 |
end; |
136 |
|
137 |
// create target table within own TX, needs to be committed separately |
138 |
SQL := SQL + format('%s TABLE %s (%s) %s', [Command, TableName, TypeDefs, CommitAction]); |
139 |
TX2 := GetAttachment.StartTransaction([isc_tpb_write, isc_tpb_nowait, isc_tpb_read_committed], taCommit); |
140 |
GetAttachment.ExecImmediate(TX2, SQL); |
141 |
TX2.Commit(); |
142 |
|
143 |
// fill target table |
144 |
SQL := format('INSERT INTO %s %s;', [TableName, SelectSQL]); |
145 |
GetAttachment.ExecImmediate(GetTransaction, SQL); |
146 |
end; |
147 |
OutputData.ByName('status').AsString := 'ok'; |
148 |
end; |
149 |
|
150 |
initialization |
151 |
|
152 |
FBRegisterUDRProcedure('select_into', TSelectInto); |
153 |
|
154 |
end. |