1 package SQL::Translator::Producer::DB2;
5 SQL::Translator::Producer::DB2 - DB2 SQL producer
11 my $t = SQL::Translator->new( parser => '...', producer => 'DB2' );
12 print $translator->translate( $file );
16 Creates an SQL DDL suitable for DB2.
22 use vars qw[ $VERSION $DEBUG $WARN ];
24 $DEBUG = 0 unless defined $DEBUG;
26 use SQL::Translator::Schema::Constants;
27 use SQL::Translator::Utils qw(header_comment);
30 # http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/ad/r0006844.htm
32 # This is a terrible WTDI, each Parser should parse down to some standard set
33 # of SQL data types, with field->extra entries being used to convert back to
34 # weird types like "polygon" if needed (IMO anyway)
43 mediumint => 'integer',
44 tinyint => 'smallint',
48 longblob => 'long varchar for bit data',
49 tinytext => 'varchar',
51 longtext => 'varchar',
52 mediumtext => 'varchar',
56 datetime => 'timestamp',
63 'double precision' => 'double',
65 bigserial => 'integer',
68 'character varying' => 'varchar',
70 interval => 'integer',
71 boolean => 'smallint',
83 'bit varying' => 'number',
89 varchar2 => 'varchar',
94 my %db2_reserved = map { $_ => 1} qw/
95 ADD DETERMINISTIC LEAVE RESTART
96 AFTER DISALLOW LEFT RESTRICT
97 ALIAS DISCONNECT LIKE RESULT
98 ALL DISTINCT LINKTYPE RESULT_SET_LOCATOR
99 ALLOCATE DO LOCAL RETURN
100 ALLOW DOUBLE LOCALE RETURNS
101 ALTER DROP LOCATOR REVOKE
102 AND DSNHATTR LOCATORS RIGHT
103 ANY DSSIZE LOCK ROLLBACK
104 APPLICATION DYNAMIC LOCKMAX ROUTINE
106 ASSOCIATE EDITPROC LONG ROWS
107 ASUTIME ELSE LOOP RRN
108 AUDIT ELSEIF MAXVALUE RUN
109 AUTHORIZATION ENCODING MICROSECOND SAVEPOINT
110 AUX END MICROSECONDS SCHEMA
111 AUXILIARY END-EXEC MINUTE SCRATCHPAD
112 BEFORE END-EXEC1 MINUTES SECOND
113 BEGIN ERASE MINVALUE SECONDS
114 BETWEEN ESCAPE MODE SECQTY
115 BINARY EXCEPT MODIFIES SECURITY
116 BUFFERPOOL EXCEPTION MONTH SELECT
117 BY EXCLUDING MONTHS SENSITIVE
118 CACHE EXECUTE NEW SET
119 CALL EXISTS NEW_TABLE SIGNAL
120 CALLED EXIT NO SIMPLE
121 CAPTURE EXTERNAL NOCACHE SOME
122 CARDINALITY FENCED NOCYCLE SOURCE
123 CASCADED FETCH NODENAME SPECIFIC
124 CASE FIELDPROC NODENUMBER SQL
125 CAST FILE NOMAXVALUE SQLID
126 CCSID FINAL NOMINVALUE STANDARD
127 CHAR FOR NOORDER START
128 CHARACTER FOREIGN NOT STATIC
130 CLOSE FROM NULLS STOGROUP
131 CLUSTER FULL NUMPARTS STORES
132 COLLECTION FUNCTION OBID STYLE
133 COLLID GENERAL OF SUBPAGES
134 COLUMN GENERATED OLD SUBSTRING
135 COMMENT GET OLD_TABLE SYNONYM
136 COMMIT GLOBAL ON SYSFUN
137 CONCAT GO OPEN SYSIBM
138 CONDITION GOTO OPTIMIZATION SYSPROC
139 CONNECT GRANT OPTIMIZE SYSTEM
140 CONNECTION GRAPHIC OPTION TABLE
141 CONSTRAINT GROUP OR TABLESPACE
142 CONTAINS HANDLER ORDER THEN
143 CONTINUE HAVING OUT TO
144 COUNT HOLD OUTER TRANSACTION
145 COUNT_BIG HOUR OVERRIDING TRIGGER
146 CREATE HOURS PACKAGE TRIM
147 CROSS IDENTITY PARAMETER TYPE
149 CURRENT_DATE IMMEDIATE PARTITION UNION
150 CURRENT_LC_CTYPE IN PATH UNIQUE
151 CURRENT_PATH INCLUDING PIECESIZE UNTIL
152 CURRENT_SERVER INCREMENT PLAN UPDATE
153 CURRENT_TIME INDEX POSITION USAGE
154 CURRENT_TIMESTAMP INDICATOR PRECISION USER
155 CURRENT_TIMEZONE INHERIT PREPARE USING
156 CURRENT_USER INNER PRIMARY VALIDPROC
157 CURSOR INOUT PRIQTY VALUES
158 CYCLE INSENSITIVE PRIVILEGES VARIABLE
159 DATA INSERT PROCEDURE VARIANT
160 DATABASE INTEGRITY PROGRAM VCAT
162 DAYS IS QUERYNO VOLUMES
163 DB2GENERAL ISOBID READ WHEN
164 DB2GENRL ISOLATION READS WHERE
165 DB2SQL ITERATE RECOVERY WHILE
166 DBINFO JAR REFERENCES WITH
167 DECLARE JAVA REFERENCING WLM
168 DEFAULT JOIN RELEASE WRITE
169 DEFAULTS KEY RENAME YEAR
170 DEFINITION LABEL REPEAT YEARS
171 DELETE LANGUAGE RESET
172 DESCRIPTOR LC_CTYPE RESIGNAL
175 #------------------------------------------------------------------------------
179 my ($translator) = @_;
180 $DEBUG = $translator->debug;
181 $WARN = $translator->show_warnings;
182 my $no_comments = $translator->no_comments;
183 my $add_drop_table = $translator->add_drop_table;
184 my $schema = $translator->schema;
188 $output .= header_comment unless($no_comments);
189 my (@table_defs, @fks, @index_defs);
190 foreach my $table ($schema->get_tables)
192 push @table_defs, 'DROP TABLE ' . $table->name . ";" if $add_drop_table;
193 my ($table_def, $fks) = create_table($table, {
194 no_comments => $no_comments});
195 push @table_defs, $table_def;
198 foreach my $index ($table->get_indices)
200 push @index_defs, create_index($index);
205 foreach my $view ( $schema->get_views )
207 push @view_defs, create_view($view);
210 foreach my $trigger ( $schema->get_triggers )
212 push @trigger_defs, create_trigger($trigger);
215 return wantarray ? (@table_defs, @fks, @index_defs, @view_defs, @trigger_defs) :
216 $output . join("\n\n", @table_defs, @fks, @index_defs, @view_defs, @trigger_defs) . "\n";
223 my ($name, $type, $length) = @_;
226 if(length($name) > $length) ## Maximum table name length is 18
228 warn "Table name $name is longer than $length characters, truncated" if $WARN;
229 # if(grep {$_ eq substr($name, 0, $length) }
230 # values(%{$objnames{$type}}))
232 # die "Got multiple matching table names when truncated";
234 # $objnames{$type}{$name} = substr($name, 0,$length);
235 # $newname = $objnames{$type}{$name};
238 if($db2_reserved{uc($newname)})
240 warn "$newname is a reserved word in DB2!" if $WARN;
243 # return sprintf("%-*s", $length-5, $newname);
250 my ($table, $options) = @_;
252 my $table_name = check_name($table->name, 'tables', 128);
253 # this limit is 18 in older DB2s ! (<= 8)
255 my (@field_defs, @comments);
256 push @comments, "--\n-- Table: $table_name\n--" unless $options->{no_comments};
257 foreach my $field ($table->get_fields)
259 push @field_defs, create_field($field);
261 my (@con_defs, @fks);
262 foreach my $con ($table->get_constraints)
264 my ($cdefs, $fks) = create_constraint($con);
265 push @con_defs, @$cdefs;
269 my $tablespace = $table->extra()->{'TABLESPACE'} || '';
270 my $table_def = "CREATE TABLE $table_name (\n";
271 $table_def .= join (",\n", map { " $_" } @field_defs, @con_defs);
273 $table_def .= $tablespace ? "IN $tablespace;" : ';';
275 return $table_def, \@fks;
282 my $field_name = check_name($field->name, 'fields', 30);
284 # print Dumper(\%dt_translate);
285 # print $field->data_type, " ", $dt_translate{lc($field->data_type)}, "\n";
286 my $data_type = uc($dt_translate{lc($field->data_type)} || $field->data_type);
287 my $size = $field->size();
289 my $field_def = "$field_name $data_type";
290 $field_def .= $field->is_auto_increment ?
291 ' GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1)' : '';
292 $field_def .= $data_type =~ /(CHAR|CLOB|NUMERIC|DECIMAL)/i ? "(${size})" : '';
293 $field_def .= !$field->is_nullable ? ' NOT NULL':'';
294 # $field_def .= $field->is_primary_key ? ' PRIMARY KEY':'';
295 $field_def .= !defined $field->default_value ? '' :
296 $field->default_value =~ /current( |_)timestamp/i ||
297 $field->default_value =~ /\Qnow()\E/i ?
298 ' DEFAULT CURRENT TIMESTAMP' : defined $field->default_value ?
299 (" DEFAULT " . ($data_type =~ /(INT|DOUBLE)/i ?
300 $field->default_value : "'" . $field->default_value . "'")
310 my $out = sprintf('CREATE %sINDEX %s ON %s ( %s );',
311 $index->type() =~ /^UNIQUE$/i ? 'UNIQUE' : '',
314 join(', ', $index->fields) );
319 sub create_constraint
321 my ($constraint) = @_;
323 my (@con_defs, @fks);
325 my $ctype = $constraint->type =~ /^PRIMARY(_|\s)KEY$/i ? 'PRIMARY KEY' :
326 $constraint->type =~ /^UNIQUE$/i ? 'UNIQUE' :
327 $constraint->type =~ /^CHECK_C$/i ? 'CHECK' :
328 $constraint->type =~ /^FOREIGN(_|\s)KEY$/i ? 'FOREIGN KEY' : '';
330 my $expr = $constraint->type =~ /^CHECK_C$/i ? $constraint->expression :
332 my $ref = $constraint->type =~ /^FOREIGN(_|\s)KEY$/i ? ('REFERENCES ' . $constraint->reference_table . '(' . join(', ', $constraint->reference_fields) . ')') : '';
333 my $update = $constraint->on_update ? $constraint->on_update : '';
334 my $delete = $constraint->on_delete ? $constraint->on_delete : '';
336 my $out = join(' ', grep { $_ }
337 $constraint->name ? ('CONSTRAINT ' . $constraint->name) : '',
339 '(' . join (', ', $constraint->fields) . ')',
340 $expr ? $expr : $ref,
343 if ($constraint->type eq FOREIGN_KEY) {
344 my $table_name = $constraint->table->name;
345 $out = "ALTER TABLE $table_name ADD $out;";
349 push @con_defs, $out;
352 return \@con_defs, \@fks;
360 my $out = sprintf("CREATE VIEW %s AS\n%s;",
370 # create: CREATE TRIGGER trigger_name before type /ON/i table_name reference_b(?) /FOR EACH ROW/i 'MODE DB2SQL' triggered_action
372 my $db_events = join ', ', $trigger->database_events;
373 my $out = sprintf('CREATE TRIGGER %s %s %s ON %s %s %s MODE DB2SQL %s',
375 $trigger->perform_action_when || 'AFTER',
376 $db_events =~ /update_on/i ?
377 ('UPDATE OF '. join(', ', $trigger->fields)) :
378 $db_events || 'UPDATE',
379 $trigger->table->name,
380 $trigger->extra->{reference} || 'REFERENCING OLD AS oldrow NEW AS newrow',
381 $trigger->extra->{granularity} || 'FOR EACH ROW',
390 my ($from_field, $to_field) = @_;
392 my $data_type = uc($dt_translate{lc($to_field->data_type)} || $to_field->data_type);
394 my $size = $to_field->size();
395 $data_type .= $data_type =~ /CHAR/i ? "(${size})" : '';
397 # DB2 will only allow changing of varchar/vargraphic datatypes
398 # to extend their lengths. Or changing of text types to other
399 # texttypes, and numeric types to larger numeric types. (v8)
400 # We can also drop/add keys, checks and constraints, but not
403 my $out = sprintf('ALTER TABLE %s ALTER %s SET DATATYPE %s',
404 $to_field->table->name,
412 my ($new_field) = @_;
414 my $out = sprintf('ALTER TABLE %s ADD COLUMN %s',
415 $new_field->table->name,
416 create_field($new_field));