activefacts-compositions 1.9.17 → 1.9.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/activefacts-compositions.gemspec +2 -2
- data/lib/activefacts/compositions/binary.rb +1 -1
- data/lib/activefacts/compositions/compositor.rb +16 -12
- data/lib/activefacts/compositions/datavault.rb +110 -115
- data/lib/activefacts/compositions/relational.rb +137 -94
- data/lib/activefacts/compositions/staging.rb +89 -27
- data/lib/activefacts/compositions/traits/datavault.rb +116 -49
- data/lib/activefacts/compositions/traits/rails.rb +2 -2
- data/lib/activefacts/compositions/version.rb +1 -1
- data/lib/activefacts/generator/doc/cwm.rb +6 -18
- data/lib/activefacts/generator/doc/ldm.rb +1 -1
- data/lib/activefacts/generator/etl/unidex.rb +341 -0
- data/lib/activefacts/generator/oo.rb +31 -14
- data/lib/activefacts/generator/rails/models.rb +6 -5
- data/lib/activefacts/generator/rails/schema.rb +5 -9
- data/lib/activefacts/generator/ruby.rb +2 -2
- data/lib/activefacts/generator/sql/mysql.rb +3 -184
- data/lib/activefacts/generator/sql/oracle.rb +3 -152
- data/lib/activefacts/generator/sql/postgres.rb +3 -145
- data/lib/activefacts/generator/sql/server.rb +3 -126
- data/lib/activefacts/generator/sql.rb +54 -422
- data/lib/activefacts/generator/summary.rb +15 -6
- data/lib/activefacts/generator/traits/expr.rb +41 -0
- data/lib/activefacts/generator/traits/sql/mysql.rb +280 -0
- data/lib/activefacts/generator/traits/sql/oracle.rb +265 -0
- data/lib/activefacts/generator/traits/sql/postgres.rb +287 -0
- data/lib/activefacts/generator/traits/sql/server.rb +262 -0
- data/lib/activefacts/generator/traits/sql.rb +538 -0
- metadata +13 -8
- data/lib/activefacts/compositions/docgraph.rb +0 -798
- data/lib/activefacts/compositions/staging/persistent.rb +0 -107
@@ -0,0 +1,280 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts MySQL Traits
|
3
|
+
#
|
4
|
+
# Copyright (c) 2009-2016 Clifford Heath. Read the LICENSE file.
|
5
|
+
#
|
6
|
+
# Reserved words gathered from:
|
7
|
+
# https://dev.mysql.com/doc/refman/5.7/en/keywords.html
|
8
|
+
#
|
9
|
+
require 'digest/sha1'
|
10
|
+
require 'activefacts/metamodel'
|
11
|
+
require 'activefacts/compositions'
|
12
|
+
require 'activefacts/generator/traits/sql'
|
13
|
+
|
14
|
+
module ActiveFacts
|
15
|
+
module Generators
|
16
|
+
module Traits
|
17
|
+
# Options are comma or space separated:
|
18
|
+
# * underscore
|
19
|
+
module SQL
|
20
|
+
module MySQL
|
21
|
+
include Traits::SQL
|
22
|
+
|
23
|
+
def options
|
24
|
+
super.merge({
|
25
|
+
# no: [String, "no new options defined here"]
|
26
|
+
})
|
27
|
+
end
|
28
|
+
|
29
|
+
def defaults_and_options options
|
30
|
+
super
|
31
|
+
end
|
32
|
+
|
33
|
+
def process_options options
|
34
|
+
super
|
35
|
+
end
|
36
|
+
|
37
|
+
def data_type_context_class
|
38
|
+
MySQLDataTypeContext
|
39
|
+
end
|
40
|
+
|
41
|
+
# See https://dev.mysql.com/doc/refman/5.7/en/identifiers.html
|
42
|
+
def table_name_max
|
43
|
+
64
|
44
|
+
end
|
45
|
+
|
46
|
+
def column_name_max
|
47
|
+
64
|
48
|
+
end
|
49
|
+
|
50
|
+
def index_name_max
|
51
|
+
64
|
52
|
+
end
|
53
|
+
|
54
|
+
def schema_name_max
|
55
|
+
64
|
56
|
+
end
|
57
|
+
|
58
|
+
def schema_prefix
|
59
|
+
''
|
60
|
+
end
|
61
|
+
|
62
|
+
def index_kind(index)
|
63
|
+
super
|
64
|
+
end
|
65
|
+
|
66
|
+
def choose_sql_type(type_name, value_constraint, component, options)
|
67
|
+
case MM::DataType.intrinsic_type(type_name)
|
68
|
+
when MM::DataType::TYPE_Integer
|
69
|
+
# The :auto_assign key is set for auto-assigned types, but with a nil value in foreign keys
|
70
|
+
if options.has_key?(:auto_assign)
|
71
|
+
options[:default] = ' AUTO_INCREMENT' if options[:auto_assign]
|
72
|
+
'BIGINT'
|
73
|
+
else
|
74
|
+
super
|
75
|
+
end
|
76
|
+
|
77
|
+
when MM::DataType::TYPE_Money
|
78
|
+
'DECIMAL'
|
79
|
+
|
80
|
+
when MM::DataType::TYPE_DateTime
|
81
|
+
'DATETIME'
|
82
|
+
|
83
|
+
when MM::DataType::TYPE_Timestamp
|
84
|
+
'DATETIME'
|
85
|
+
|
86
|
+
when MM::DataType::TYPE_Binary
|
87
|
+
case binary_surrogate(type_name, value_constraint, options)
|
88
|
+
when :guid_fk # A GUID surrogate that's auto-assigned elsewhere
|
89
|
+
options[:length] = 16
|
90
|
+
when :guid # A GUID
|
91
|
+
options[:length] = 16
|
92
|
+
options[:default] = " DEFAULT UNHEX(REPLACE(UUID(),'-',''))"
|
93
|
+
when :hash # A hash of the natural key
|
94
|
+
options[:length] = 20 # Assuming SHA-1. SHA-256 would need 32 bytes
|
95
|
+
leaves = component.root.natural_index.all_index_field.map(&:component)
|
96
|
+
# NDB Only, not InnoDB:
|
97
|
+
# options[:default] = " GENERATED ALWAYS AS (#{hash(concatenate(coalesce(as_text(safe_column_exprs(leaves)))))}) STORED"
|
98
|
+
options[:delayed] = trigger_hash_assignment(component, component.root.natural_index.all_index_field.map(&:component))
|
99
|
+
else # Not a surrogate
|
100
|
+
# MySQL has various non-standard blob types also
|
101
|
+
end
|
102
|
+
'BINARY'
|
103
|
+
|
104
|
+
else
|
105
|
+
super
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Return an array of SQL statements that arrange for the hash_field
|
110
|
+
# to be populated with a hash of the values of the leaves.
|
111
|
+
def trigger_hash_assignment hash_field, leaves
|
112
|
+
table_name = safe_table_name(hash_field.root)
|
113
|
+
trigger_function = escape('assign_'+column_name(hash_field), 128)
|
114
|
+
[
|
115
|
+
%Q{
|
116
|
+
CREATE TRIGGER #{trigger_function} BEFORE INSERT ON #{table_name}
|
117
|
+
FOR EACH ROW SET #{safe_column_name(hash_field)} = #{
|
118
|
+
hash(concatenate(coalesce(as_text(safe_column_exprs(leaves, 'NEW')))))
|
119
|
+
}}.unindent
|
120
|
+
]
|
121
|
+
end
|
122
|
+
|
123
|
+
# Some or all of the SQL expressions may have non-text values.
|
124
|
+
# Return an SQL expression that coerces them to text.
|
125
|
+
def as_text exprs
|
126
|
+
return exprs.map{|e| as_text(e)} if Array === exprs
|
127
|
+
|
128
|
+
Expression.new("CAST(#{exprs} AS CHAR)", MM::DataType::TYPE_String, exprs.is_mandatory)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Return an SQL expression that concatenates the given expressions (which must yield a string type)
|
132
|
+
def concatenate expressions
|
133
|
+
Expression.new(
|
134
|
+
"CONCAT('|', #{expressions.map(&:to_s)*', '})",
|
135
|
+
MM::DataType::TYPE_String,
|
136
|
+
true
|
137
|
+
)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Return an expression that yields a hash of the given expression
|
141
|
+
# SHA1 produces 40 hexadecimal digits
|
142
|
+
def hash expr, algo = nil
|
143
|
+
Expression.new("UNHEX(SHA1(#{expr}))", MM::DataType::TYPE_Binary, expr.is_mandatory)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Reserved words cannot be used anywhere without quoting.
|
147
|
+
# Keywords have existing definitions, so should not be used without quoting.
|
148
|
+
# Both lists here are added to the supertype's lists
|
149
|
+
def reserved_words
|
150
|
+
@mysql_reserved_words ||= %w{
|
151
|
+
ACCESSIBLE ANALYZE CHANGE DATABASE DATABASES DAY_HOUR
|
152
|
+
DAY_MICROSECOND DAY_MINUTE DAY_SECOND DELAYED DISTINCTROW
|
153
|
+
DIV DUAL ENCLOSED ESCAPED EXPLAIN FLOAT4 FLOAT8 FORCE
|
154
|
+
FULLTEXT HIGH_PRIORITY HOUR_MICROSECOND HOUR_MINUTE
|
155
|
+
HOUR_SECOND INDEX INFILE INT1 INT2 INT3 INT4 INT8
|
156
|
+
IO_AFTER_GTIDS IO_BEFORE_GTIDS KEYS KILL LINEAR LINES
|
157
|
+
LOAD LOCK LONG LONGBLOB LONGTEXT LOW_PRIORITY MASTER_BIND
|
158
|
+
MASTER_SSL_VERIFY_SERVER_CERT MEDIUMBLOB MEDIUMINT
|
159
|
+
MEDIUMTEXT MIDDLEINT MINUTE_MICROSECOND MINUTE_SECOND
|
160
|
+
NO_WRITE_TO_BINLOG OPTIMIZE OPTIMIZER_COSTS OPTIONALLY
|
161
|
+
OUTFILE PURGE READ_WRITE REGEXP RENAME REPLACE REQUIRE
|
162
|
+
RLIKE SCHEMAS SECOND_MICROSECOND SEPARATOR SHOW SPATIAL
|
163
|
+
SQL_BIG_RESULT SQL_CALC_FOUND_ROWS SQL_SMALL_RESULT SSL
|
164
|
+
STARTING STORED STRAIGHT_JOIN TERMINATED TINYBLOB TINYINT
|
165
|
+
TINYTEXT UNLOCK UNSIGNED USE UTC_DATE UTC_TIME UTC_TIMESTAMP
|
166
|
+
VARCHARACTER VIRTUAL XOR YEAR_MONTH ZEROFILL _FILENAME
|
167
|
+
}
|
168
|
+
super + @mysql_reserved_words
|
169
|
+
end
|
170
|
+
|
171
|
+
def key_words
|
172
|
+
# These keywords should not be used for columns or tables:
|
173
|
+
@mysql_key_words ||= %w{
|
174
|
+
ACCOUNT AGAINST AGGREGATE ALGORITHM ANALYSE ASCII
|
175
|
+
AUTOEXTEND_SIZE AUTO_INCREMENT AVG_ROW_LENGTH BACKUP
|
176
|
+
BINLOG BLOCK BOOL BTREE BYTE CACHE CHANGED CHANNEL
|
177
|
+
CHARSET CHECKSUM CIPHER CLIENT CODE COLUMN_FORMAT COMMENT
|
178
|
+
COMPACT COMPLETION COMPRESSED COMPRESSION CONCURRENT
|
179
|
+
CONSISTENT CONTEXT CPU DATAFILE DATETIME DEFAULT_AUTH
|
180
|
+
DELAY_KEY_WRITE DES_KEY_FILE DIRECTORY DISABLE DISCARD
|
181
|
+
DISK DUMPFILE DUPLICATE ENABLE ENCRYPTION ENDS ENGINE
|
182
|
+
ENGINES ENUM ERROR ERRORS EVENT EVENTS EXCHANGE EXPANSION
|
183
|
+
EXPIRE EXPORT EXTENDED EXTENT_SIZE FAST FAULTS FIELDS
|
184
|
+
FILE_BLOCK_SIZE FIXED FLUSH FOLLOWS FORMAT GEOMETRY
|
185
|
+
GEOMETRYCOLLECTION GET_FORMAT GRANTS GROUP_REPLICATION
|
186
|
+
HASH HELP HOST HOSTS IDENTIFIED IGNORE_SERVER_IDS INDEXES
|
187
|
+
INITIAL_SIZE INSERT_METHOD INSTALL IO IO_THREAD IPC
|
188
|
+
ISSUER JSON KEY_BLOCK_SIZE LEAVES LESS LINESTRING LIST
|
189
|
+
LOCKS LOGFILE LOGS MASTER MASTER_AUTO_POSITION
|
190
|
+
MASTER_CONNECT_RETRY MASTER_DELAY MASTER_HEARTBEAT_PERIOD
|
191
|
+
MASTER_HOST MASTER_LOG_FILE MASTER_LOG_POS MASTER_PASSWORD
|
192
|
+
MASTER_PORT MASTER_RETRY_COUNT MASTER_SERVER_ID MASTER_SSL
|
193
|
+
MASTER_SSL_CA MASTER_SSL_CAPATH MASTER_SSL_CERT
|
194
|
+
MASTER_SSL_CIPHER MASTER_SSL_CRL MASTER_SSL_CRLPATH
|
195
|
+
MASTER_SSL_KEY MASTER_TLS_VERSION MASTER_USER
|
196
|
+
MAX_CONNECTIONS_PER_HOUR MAX_QUERIES_PER_HOUR MAX_ROWS
|
197
|
+
MAX_SIZE MAX_STATEMENT_TIME MAX_UPDATES_PER_HOUR
|
198
|
+
MAX_USER_CONNECTIONS MEDIUM MEMORY MICROSECOND MIGRATE
|
199
|
+
MIN_ROWS MODE MODIFY MULTILINESTRING MULTIPOINT
|
200
|
+
MULTIPOLYGON MUTEX MYSQL_ERRNO NDB NDBCLUSTER NEVER
|
201
|
+
NODEGROUP NONBLOCKING NO_WAIT NVARCHAR OLD_PASSWORD ONE
|
202
|
+
OWNER PACK_KEYS PAGE PARSER PARSE_GCOL_EXPR PARTITIONING
|
203
|
+
PARTITIONS PASSWORD PHASE PLUGIN PLUGINS PLUGIN_DIR
|
204
|
+
POINT POLYGON PORT PREV PROCESSLIST PROFILE PROFILES
|
205
|
+
PROXY QUARTER QUERY QUICK READ_ONLY REBUILD RECOVER
|
206
|
+
REDOFILE REDO_BUFFER_SIZE REDUNDANT RELAY RELAYLOG
|
207
|
+
RELAY_LOG_FILE RELAY_LOG_POS RELAY_THREAD RELOAD REMOVE
|
208
|
+
REORGANIZE REPAIR REPLICATE_DO_DB REPLICATE_DO_TABLE
|
209
|
+
REPLICATE_IGNORE_DB REPLICATE_IGNORE_TABLE REPLICATE_REWRITE_DB
|
210
|
+
REPLICATE_WILD_DO_TABLE REPLICATE_WILD_IGNORE_TABLE
|
211
|
+
REPLICATION RESET RESUME REVERSE ROTATE ROW_FORMAT RTREE
|
212
|
+
SCHEDULE SERIAL SHARE SHUTDOWN SIGNED SLAVE SLOW SNAPSHOT
|
213
|
+
SOCKET SONAME SOUNDS SQL_AFTER_GTIDS SQL_AFTER_MTS_GAPS
|
214
|
+
SQL_BEFORE_GTIDS SQL_BUFFER_RESULT SQL_CACHE SQL_NO_CACHE
|
215
|
+
SQL_THREAD SQL_TSI_DAY SQL_TSI_HOUR SQL_TSI_MINUTE
|
216
|
+
SQL_TSI_MONTH SQL_TSI_QUARTER SQL_TSI_SECOND SQL_TSI_WEEK
|
217
|
+
SQL_TSI_YEAR STACKED STARTS STATS_AUTO_RECALC
|
218
|
+
STATS_PERSISTENT STATS_SAMPLE_PAGES STATUS STOP STORAGE
|
219
|
+
STRING SUBJECT SUBPARTITION SUBPARTITIONS SUPER SUSPEND
|
220
|
+
SWAPS SWITCHES TABLES TABLESPACE TABLE_CHECKSUM TEMPTABLE
|
221
|
+
TEXT THAN TIMESTAMPADD TIMESTAMPDIFF TRIGGERS TYPES
|
222
|
+
UNDEFINED UNDOFILE UNDO_BUFFER_SIZE UNICODE UNINSTALL
|
223
|
+
UPGRADE USER_RESOURCES USE_FRM VALIDATION VARIABLES
|
224
|
+
WAIT WARNINGS WEEK WEIGHT_STRING X509 XA XID
|
225
|
+
}
|
226
|
+
super + @mysql_key_words
|
227
|
+
end
|
228
|
+
|
229
|
+
def open_escape
|
230
|
+
'`'
|
231
|
+
end
|
232
|
+
|
233
|
+
def close_escape
|
234
|
+
'`'
|
235
|
+
end
|
236
|
+
|
237
|
+
def index_kind(index)
|
238
|
+
''
|
239
|
+
end
|
240
|
+
|
241
|
+
class MySQLDataTypeContext < SQLDataTypeContext
|
242
|
+
def integer_ranges
|
243
|
+
[
|
244
|
+
['TINYINT', -2**7, 2**7-1],
|
245
|
+
['TINYINT UNSIGNED', 0, 2**8-1],
|
246
|
+
['MEDIUMINT', -2**23, 2**23-1],
|
247
|
+
] + super
|
248
|
+
end
|
249
|
+
|
250
|
+
# MySQL also has a BIT type, but you can specify a size up to BIT(64). This is better.
|
251
|
+
def boolean_type
|
252
|
+
'BOOLEAN' # Which is a synonym for TINYINT(1), stores 0 or 1
|
253
|
+
end
|
254
|
+
|
255
|
+
def boolean_expr safe_column_name
|
256
|
+
"#{safe_column_name} = 1"
|
257
|
+
end
|
258
|
+
|
259
|
+
def default_char_type
|
260
|
+
(@unicode ? 'N' : '') +
|
261
|
+
'CHAR'
|
262
|
+
end
|
263
|
+
|
264
|
+
def default_varchar_type
|
265
|
+
(@unicode ? 'N' : '') +
|
266
|
+
'VARCHAR'
|
267
|
+
end
|
268
|
+
|
269
|
+
def date_time_type
|
270
|
+
# MySQL also has TIMESTAMP values, but those run from 1970/01/01 through 2038/01/19
|
271
|
+
# DATETIME runs from 1000/01/01 through 9999/01/01
|
272
|
+
'DATETIME'
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
@@ -0,0 +1,265 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts Oracle SQL Traits
|
3
|
+
#
|
4
|
+
# Copyright (c) 2009-2016 Clifford Heath. Read the LICENSE file.
|
5
|
+
#
|
6
|
+
# Reserved words gathered from:
|
7
|
+
# https://docs.oracle.com/cd/B28359_01/appdev.111/b31231/appb.htm
|
8
|
+
#
|
9
|
+
require 'digest/sha1'
|
10
|
+
require 'activefacts/metamodel'
|
11
|
+
require 'activefacts/compositions'
|
12
|
+
require 'activefacts/generator/traits/sql'
|
13
|
+
|
14
|
+
module ActiveFacts
|
15
|
+
module Generators
|
16
|
+
module Traits
|
17
|
+
# Options are comma or space separated:
|
18
|
+
# * underscore
|
19
|
+
module SQL
|
20
|
+
module Oracle
|
21
|
+
include Traits::SQL
|
22
|
+
|
23
|
+
# Options available in this flavour of SQL
|
24
|
+
def options
|
25
|
+
super.merge({
|
26
|
+
# no: [String, "no new options defined here"]
|
27
|
+
})
|
28
|
+
end
|
29
|
+
|
30
|
+
# The options parameter overrides any default options set by sub-traits
|
31
|
+
def defaults_and_options options
|
32
|
+
{'tables' => 'shout', 'columns' => 'shout'}.merge(options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def process_options options
|
36
|
+
super
|
37
|
+
end
|
38
|
+
|
39
|
+
def data_type_context_class
|
40
|
+
OracleDataTypeContext
|
41
|
+
end
|
42
|
+
|
43
|
+
# See https://docs.oracle.com/database/122/NEWFT/new-features.htm
|
44
|
+
# Identifier lengths were 30 *bytes* prior to Oracle 12C
|
45
|
+
def table_name_max
|
46
|
+
128
|
47
|
+
end
|
48
|
+
|
49
|
+
def column_name_max
|
50
|
+
128
|
51
|
+
end
|
52
|
+
|
53
|
+
def index_name_max
|
54
|
+
128
|
55
|
+
end
|
56
|
+
|
57
|
+
def schema_name_max
|
58
|
+
128 # Was 8 characters
|
59
|
+
end
|
60
|
+
|
61
|
+
def schema_prefix
|
62
|
+
''
|
63
|
+
end
|
64
|
+
|
65
|
+
def index_kind(index)
|
66
|
+
super
|
67
|
+
end
|
68
|
+
|
69
|
+
def choose_sql_type(type_name, value_constraint, component, options)
|
70
|
+
type = MM::DataType.intrinsic_type(type_name)
|
71
|
+
case type
|
72
|
+
when MM::DataType::TYPE_Integer
|
73
|
+
# The :auto_assign key is set for auto-assigned types, but with a nil value in foreign keys
|
74
|
+
if options.has_key?(:auto_assign)
|
75
|
+
options[:default] = ' GENERATED BY DEFAULT ON NULL AS IDENTITY' if options[:auto_assign]
|
76
|
+
'LONGINTEGER'
|
77
|
+
else
|
78
|
+
super
|
79
|
+
end
|
80
|
+
|
81
|
+
when MM::DataType::TYPE_Money
|
82
|
+
'MONEY'
|
83
|
+
|
84
|
+
when MM::DataType::TYPE_DateTime
|
85
|
+
'DATETIME'
|
86
|
+
|
87
|
+
when MM::DataType::TYPE_Timestamp
|
88
|
+
'DATETIME'
|
89
|
+
|
90
|
+
when MM::DataType::TYPE_Binary
|
91
|
+
case binary_surrogate(type_name, value_constraint, options)
|
92
|
+
when :guid_fk # A surrogate that's auto-assigned elsewhere
|
93
|
+
options[:length] = 16 # This will display as 32 hex digits
|
94
|
+
'RAW'
|
95
|
+
when :guid # A GUID
|
96
|
+
options[:length] = 16
|
97
|
+
options[:default] = " DEFAULT SYS_GUID()"
|
98
|
+
'RAW'
|
99
|
+
when :hash # A hash of the natural key
|
100
|
+
options[:length] = 20 # Assuming SHA-1. SHA-256 would need 32 bytes
|
101
|
+
leaves = component.root.natural_index.all_index_field.map(&:component)
|
102
|
+
options[:default] = " GENERATED ALWAYS AS #{hash(concatenate(coalesce(as_text(safe_column_exprs(leaves)))))}"
|
103
|
+
'RAW'
|
104
|
+
else
|
105
|
+
'LOB' # Not a surrogate, just use the type
|
106
|
+
end
|
107
|
+
else
|
108
|
+
super
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
=begin
|
113
|
+
# Return an array of SQL statements that arrange for the hash_field
|
114
|
+
# to be populated with a hash of the values of the leaves.
|
115
|
+
def trigger_hash_assignment hash_field, leaves
|
116
|
+
table_name = safe_table_name(hash_field.root)
|
117
|
+
trigger_function = escape('assign_'+column_name(hash_field), 128)
|
118
|
+
[
|
119
|
+
%Q{
|
120
|
+
CREATE OR REPLACE
|
121
|
+
FUNCTION #{trigger_function}() RETURN RAW DETERMINISTIC
|
122
|
+
AS
|
123
|
+
PRAGMA UDF;
|
124
|
+
BEGIN
|
125
|
+
RETURN #{hash(concatenate(coalesce(as_text(safe_column_exprs(leaves, 'NEW')))))};
|
126
|
+
END #{trigger_function}}.
|
127
|
+
unindent,
|
128
|
+
%Q{ -- This is the Postgres syntax
|
129
|
+
CREATE TRIGGER trig_#{trigger_function}
|
130
|
+
BEFORE INSERT OR UPDATE ON #{table_name}
|
131
|
+
FOR EACH ROW EXECUTE PROCEDURE #{trigger_function}()}.
|
132
|
+
unindent
|
133
|
+
]
|
134
|
+
end
|
135
|
+
=end
|
136
|
+
|
137
|
+
# Some or all of the SQL expressions may have non-text values.
|
138
|
+
# Return an SQL expression that coerces them to text.
|
139
|
+
def as_text exprs
|
140
|
+
return exprs.map{|e| as_text(e)} if Array === exprs
|
141
|
+
|
142
|
+
Expression.new("#{exprs}", MM::DataType::TYPE_String, exprs.is_mandatory)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Return an SQL expression that concatenates the given expressions (which must yield a string type)
|
146
|
+
def concatenate expressions
|
147
|
+
Expression.new(
|
148
|
+
"'|' || " +
|
149
|
+
expressions.map(&:to_s) * " || '|' || " +
|
150
|
+
" || '|'",
|
151
|
+
MM::DataType::TYPE_String,
|
152
|
+
true
|
153
|
+
)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Return an expression that yields a hash of the given expression
|
157
|
+
def hash expr, algo = 'SHA-1'
|
158
|
+
# Since Oracle 12.1:
|
159
|
+
Expression.new("STANDARD_HASH(#{expr}, '#{algo}')", MM::DataType::TYPE_Binary, expr.is_mandatory)
|
160
|
+
|
161
|
+
# Expression.new("utl_raw.cast_to_raw(dbms_crypto.hash(#{expr}, dbms_crypto.hash_#{algo}))", MM::DataType::TYPE_Binary, expr.is_mandatory)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Reserved words cannot be used anywhere without quoting.
|
165
|
+
# Keywords have existing definitions, so should not be used without quoting.
|
166
|
+
# Both lists here are added to the supertype's lists
|
167
|
+
def reserved_words
|
168
|
+
@oracle_reserved_words ||= %w{
|
169
|
+
ACCESS ARRAYLEN AUDIT CLUSTER COMMENT COMPRESS EXCLUSIVE
|
170
|
+
IDENTIFIED INDEX INITIAL LOCK LONG MAXEXTENTS MINUS
|
171
|
+
MODE MODIFY NOAUDIT NOCOMPRESS NOTFOUND NOWAIT OFFLINE
|
172
|
+
ONLINE PCTFREE RAW RENAME RESOURCE ROWID ROWLABEL ROWNUM
|
173
|
+
SHARE SQLBUF SUCCESSFUL SYNONYM SYSDATE UID VALIDATE
|
174
|
+
VARCHAR2
|
175
|
+
}
|
176
|
+
@oracle_plsql_reserved_words ||= %w{
|
177
|
+
ABORT ACCEPT ACCESS ARRAYLEN ASSERT ASSIGN BASE_TABLE
|
178
|
+
BINARY_INTEGER BODY CHAR_BASE CLUSTER CLUSTERS COLAUTH
|
179
|
+
COMPRESS CONSTANT CRASH CURRVAL DATABASE DATA_BASE DBA
|
180
|
+
DEBUGOFF DEBUGON DEFINITION DELAY DELTA DIGITS DISPOSE
|
181
|
+
ELSIF ENTRY EXCEPTION_INIT FORM GENERIC IDENTIFIED INDEX
|
182
|
+
INDEXES LIMITED MINUS MLSLABEL MODE NEXTVAL NOCOMPRESS
|
183
|
+
NUMBER_BASE PACKAGE PCTFREE POSITIVE PRAGMA PRIVATE
|
184
|
+
RAISE RECORD REMR RENAME RESOURCE REVERSE ROWID ROWLABEL
|
185
|
+
ROWNUM ROWTYPE RUN SEPARATE SQLERRM STDDEV SUBTYPE
|
186
|
+
TABAUTH TABLES TASK TERMINATE USE VARCHAR2 VARIANCE
|
187
|
+
VIEWS XOR
|
188
|
+
}
|
189
|
+
super + @oracle_reserved_words
|
190
|
+
end
|
191
|
+
|
192
|
+
def key_words
|
193
|
+
# These keywords should not be used for columns or tables:
|
194
|
+
@oracle_key_words ||= %w{
|
195
|
+
ANALYZE ARCHIVE ARCHIVELOG BACKUP BECOME BLOCK BODY
|
196
|
+
CACHE CANCEL CHANGE CHECKPOINT COMPILE CONTENTS CONTROLFILE
|
197
|
+
DATABASE DATAFILE DBA DISABLE DISMOUNT DUMP ENABLE
|
198
|
+
EVENTS EXCEPTIONS EXPLAIN EXTENT EXTERNALLY FLUSH FORCE
|
199
|
+
FREELIST FREELISTS INITRANS LAYER LISTS LOGFILE MANAGE
|
200
|
+
MANUAL MAXDATAFILES MAXINSTANCES MAXLOGFILES MAXLOGHISTORY
|
201
|
+
MAXLOGMEMBERS MAXTRANS MINEXTENTS MOUNT NOARCHIVELOG
|
202
|
+
NOCACHE NOCYCLE NOMAXVALUE NOMINVALUE NOORDER NORESETLOGS
|
203
|
+
NORMAL NOSORT OPTIMAL OWN PACKAGE PARALLEL PCTINCREASE
|
204
|
+
PCTUSED PLAN PRIVATE PROFILE QUOTA RECOVER RESETLOGS
|
205
|
+
RESTRICTED REUSE ROLES SCN SEGMENT SHARED SNAPSHOT SORT
|
206
|
+
STATEMENT_ID STATISTICS STOP STORAGE SWITCH TABLES
|
207
|
+
TABLESPACE THREAD TRACING TRIGGERS UNLIMITED USE
|
208
|
+
}
|
209
|
+
super + @oracle_key_words
|
210
|
+
end
|
211
|
+
|
212
|
+
def open_escape
|
213
|
+
'"'
|
214
|
+
end
|
215
|
+
|
216
|
+
def close_escape
|
217
|
+
'"'
|
218
|
+
end
|
219
|
+
|
220
|
+
def index_kind(index)
|
221
|
+
''
|
222
|
+
end
|
223
|
+
|
224
|
+
class OracleDataTypeContext < SQLDataTypeContext
|
225
|
+
def integer_ranges
|
226
|
+
[
|
227
|
+
['SHORTINTEGER', -2**15, 2**15-1], # The standard says -10^5..10^5 (less than 16 bits)
|
228
|
+
['INTEGER', -2**31, 2**31-1], # The standard says -10^10..10^10 (more than 32 bits!)
|
229
|
+
['LONGINTEGER', -2**63, 2**63-1], # The standard says -10^19..10^19 (less than 64 bits)
|
230
|
+
]
|
231
|
+
end
|
232
|
+
|
233
|
+
# PL/SQL has a BOOLEAN type, but Oracle databases do not, see
|
234
|
+
# https://docs.oracle.com/database/121/LNPLS/datatypes.htm#LNPLS348
|
235
|
+
def boolean_type
|
236
|
+
'CHAR(1)' # Probably should put a CHECK '0' or '1' here
|
237
|
+
end
|
238
|
+
|
239
|
+
# Ugly, but safe (Oracle's internal schema tables use 'Y' and 'N'):
|
240
|
+
def boolean_expr safe_column_name
|
241
|
+
"(#{safe_column_name} = '1' OR #{safe_column_name} = 'Y')"
|
242
|
+
end
|
243
|
+
|
244
|
+
# There is no performance benefit in using fixed-length CHAR fields,
|
245
|
+
# and an added burden of trimming the implicitly added white-space
|
246
|
+
def default_char_type
|
247
|
+
(@unicode ? 'N' : '') +
|
248
|
+
'VARCHAR'
|
249
|
+
end
|
250
|
+
|
251
|
+
def default_varchar_type
|
252
|
+
(@unicode ? 'N' : '') +
|
253
|
+
'VARCHAR'
|
254
|
+
end
|
255
|
+
|
256
|
+
def date_time_type
|
257
|
+
'TIMESTAMP'
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|