@dbml/connector 3.7.4 → 3.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable camelcase */
2
2
  import { Client } from 'pg';
3
+ import { buildSchemaQuery, parseConnectionString } from '../utils/parseSchema';
3
4
  import {
4
5
  DatabaseSchema,
5
6
  DefaultInfo,
@@ -113,14 +114,14 @@ const generateField = (row: Record<string, any>): Field => {
113
114
  };
114
115
  };
115
116
 
116
- const generateTablesAndFields = async (client: Client): Promise<{
117
+ const generateTablesAndFields = async (client: Client, schemas: string[]): Promise<{
117
118
  tables: Table[],
118
119
  fields: FieldsDictionary,
119
120
  }> => {
120
121
  const fields: FieldsDictionary = {};
121
122
  const tablesAndFieldsSql = `
122
123
  WITH comments AS (
123
- SELECT
124
+ SELECT DISTINCT ON (pc.relname, pn.nspname, pa.attname)
124
125
  pc.relname AS table_name,
125
126
  pn.nspname AS table_schema,
126
127
  pa.attname AS column_name,
@@ -150,6 +151,7 @@ const generateTablesAndFields = async (client: Client): Promise<{
150
151
  c.identity_increment,
151
152
  c.is_nullable,
152
153
  c.column_default,
154
+ c.ordinal_position,
153
155
  CASE
154
156
  WHEN c.column_default IS NULL THEN NULL
155
157
  WHEN c.column_default LIKE 'nextval(%' THEN 'increment'
@@ -158,8 +160,8 @@ const generateTablesAndFields = async (client: Client): Promise<{
158
160
  WHEN c.column_default ~ '^-?[0-9]+(.[0-9]+)?$' THEN 'number'
159
161
  ELSE 'expression'
160
162
  END AS default_type,
161
- (SELECT description FROM comments WHERE table_name = t.table_name AND table_schema = t.table_schema AND column_name IS NULL) AS table_comment,
162
- (SELECT description FROM comments WHERE table_name = t.table_name AND table_schema = t.table_schema AND column_name = c.column_name) AS column_comment
163
+ (SELECT description FROM comments WHERE table_name = t.table_name AND table_schema = t.table_schema AND column_name IS NULL LIMIT 1) AS table_comment,
164
+ (SELECT description FROM comments WHERE table_name = t.table_name AND table_schema = t.table_schema AND column_name = c.column_name LIMIT 1) AS column_comment
163
165
  FROM
164
166
  information_schema.columns c
165
167
  JOIN
@@ -167,6 +169,7 @@ const generateTablesAndFields = async (client: Client): Promise<{
167
169
  WHERE
168
170
  t.table_type = 'BASE TABLE'
169
171
  AND t.table_schema NOT IN ('pg_catalog', 'information_schema')
172
+ ${buildSchemaQuery('t.table_schema', schemas)}
170
173
  ORDER BY
171
174
  t.table_schema,
172
175
  t.table_name,
@@ -200,7 +203,7 @@ const generateTablesAndFields = async (client: Client): Promise<{
200
203
  };
201
204
  };
202
205
 
203
- const generateRawRefs = async (client: Client): Promise<Ref[]> => {
206
+ const generateRawRefs = async (client: Client, schemas: string[]): Promise<Ref[]> => {
204
207
  const refs: Ref[] = [];
205
208
 
206
209
  const refsListSql = `
@@ -226,6 +229,7 @@ const generateRawRefs = async (client: Client): Promise<Ref[]> => {
226
229
  AND tc.table_schema = rc.constraint_schema
227
230
  WHERE tc.constraint_type = 'FOREIGN KEY'
228
231
  AND tc.table_schema NOT IN ('pg_catalog', 'information_schema')
232
+ ${buildSchemaQuery('tc.table_schema', schemas)}
229
233
  GROUP BY
230
234
  tc.table_schema,
231
235
  tc.table_name,
@@ -279,8 +283,7 @@ const generateRawRefs = async (client: Client): Promise<Ref[]> => {
279
283
  return refs;
280
284
  };
281
285
 
282
- const generateIndexes = async (client: Client) => {
283
- // const tableConstraints = {};
286
+ const generateIndexes = async (client: Client, schemas: string[]) => {
284
287
  const indexListSql = `
285
288
  WITH user_tables AS (
286
289
  SELECT
@@ -293,6 +296,7 @@ const generateIndexes = async (client: Client) => {
293
296
  ),
294
297
  index_info AS (
295
298
  SELECT
299
+ t.relnamespace::regnamespace::text AS table_schema,
296
300
  t.relname AS table_name,
297
301
  i.relname AS index_name,
298
302
  ix.indisunique AS is_unique,
@@ -317,7 +321,7 @@ const generateIndexes = async (client: Client) => {
317
321
  AND t.relname NOT LIKE 'pg_%'
318
322
  AND t.relname NOT LIKE 'sql_%'
319
323
  GROUP BY
320
- t.relname, i.relname, ix.indisunique, ix.indisprimary, am.amname, ix.indexprs, ix.indrelid
324
+ t.relnamespace, t.relname, i.relname, ix.indisunique, ix.indisprimary, am.amname, ix.indexprs, ix.indrelid
321
325
  )
322
326
  SELECT
323
327
  ut.tableschema AS table_schema,
@@ -328,12 +332,13 @@ const generateIndexes = async (client: Client) => {
328
332
  ii.index_type,
329
333
  ii.columns,
330
334
  ii.expressions,
331
- ii.constraint_type -- Added constraint type
335
+ ii.constraint_type -- Retained constraint type here
332
336
  FROM
333
337
  user_tables ut
334
338
  LEFT JOIN
335
- index_info ii ON ut.tablename = ii.table_name
339
+ index_info ii ON ut.tableschema = ii.table_schema AND ut.tablename = ii.table_name
336
340
  WHERE ii.columns IS NOT NULL
341
+ ${buildSchemaQuery('ut.tableschema', schemas)}
337
342
  ORDER BY
338
343
  ut.tablename,
339
344
  ii.constraint_type,
@@ -427,9 +432,9 @@ const generateIndexes = async (client: Client) => {
427
432
  };
428
433
  };
429
434
 
430
- const generateRawEnums = async (client: Client): Promise<Enum[]> => {
435
+ const generateRawEnums = async (client: Client, schemas: string[]): Promise<Enum[]> => {
431
436
  const enumListSql = `
432
- SELECT
437
+ SELECT DISTINCT
433
438
  n.nspname AS schema_name,
434
439
  t.typname AS enum_type,
435
440
  e.enumlabel AS enum_value,
@@ -440,6 +445,7 @@ const generateRawEnums = async (client: Client): Promise<Enum[]> => {
440
445
  pg_type t ON e.enumtypid = t.oid
441
446
  JOIN
442
447
  pg_namespace n ON t.typnamespace = n.oid
448
+ ${buildSchemaQuery('n.nspname', schemas, 'WHERE')}
443
449
  ORDER BY
444
450
  schema_name,
445
451
  enum_type,
@@ -449,15 +455,16 @@ const generateRawEnums = async (client: Client): Promise<Enum[]> => {
449
455
  const enumListResult = await client.query(enumListSql);
450
456
  const enums = enumListResult.rows.reduce((acc, row) => {
451
457
  const { schema_name, enum_type, enum_value } = row;
458
+ const key = `${schema_name}.${enum_type}`;
452
459
 
453
- if (!acc[enum_type]) {
454
- acc[enum_type] = {
460
+ if (!acc[key]) {
461
+ acc[key] = {
455
462
  name: enum_type,
456
463
  schemaName: schema_name,
457
464
  values: [],
458
465
  };
459
466
  }
460
- acc[enum_type].values.push({
467
+ acc[key].values.push({
461
468
  name: enum_value,
462
469
  });
463
470
  return acc;
@@ -467,12 +474,13 @@ const generateRawEnums = async (client: Client): Promise<Enum[]> => {
467
474
  };
468
475
 
469
476
  const fetchSchemaJson = async (connection: string): Promise<DatabaseSchema> => {
470
- const client = await getValidatedClient(connection);
477
+ const { connectionString, schemas } = parseConnectionString(connection, 'jdbc');
478
+ const client = await getValidatedClient(connectionString);
471
479
 
472
- const tablesAndFieldsRes = generateTablesAndFields(client);
473
- const indexesRes = generateIndexes(client);
474
- const refsRes = generateRawRefs(client);
475
- const enumsRes = generateRawEnums(client);
480
+ const tablesAndFieldsRes = generateTablesAndFields(client, schemas);
481
+ const indexesRes = generateIndexes(client, schemas);
482
+ const refsRes = generateRawRefs(client, schemas);
483
+ const enumsRes = generateRawEnums(client, schemas);
476
484
 
477
485
  const res = await Promise.all([
478
486
  tablesAndFieldsRes,
@@ -498,5 +506,5 @@ const fetchSchemaJson = async (connection: string): Promise<DatabaseSchema> => {
498
506
  };
499
507
 
500
508
  export {
501
- fetchSchemaJson,
509
+ fetchSchemaJson
502
510
  };
@@ -0,0 +1,370 @@
1
+ /* eslint-disable camelcase */
2
+ //
3
+ // Description:
4
+ // Snowflake info schema lacks reference direction for relationships.
5
+ // Constraints and uniqueness cannot be auto-generated from Snowflake info schema.
6
+ // Snowflake does not support ENUMs.
7
+ // Snowflake does not support indexes for standard tables.
8
+ //
9
+
10
+ import snowflake, { Connection, LogLevel } from 'snowflake-sdk';
11
+ import { parseSchema } from '../utils/parseSchema';
12
+ import {
13
+ DatabaseSchema,
14
+ DefaultInfo,
15
+ DefaultType,
16
+ Field,
17
+ FieldsDictionary,
18
+ Index,
19
+ IndexesDictionary,
20
+ Table,
21
+ TableConstraintsDictionary,
22
+ } from './types';
23
+
24
+ type ConstraintRow = {
25
+ schemaName: string;
26
+ tableName: string;
27
+ constraintName: string;
28
+ columnNames: string[];
29
+ type: string;
30
+ primary?: boolean;
31
+ unique?: boolean;
32
+ };
33
+
34
+ type GeneratedIndexes = {
35
+ indexes: IndexesDictionary;
36
+ tableConstraints: TableConstraintsDictionary;
37
+ };
38
+
39
+ const parseConnectionString = (connectionString: string): Record<string, string> => {
40
+ const params: Record<string, string> = {};
41
+ const regex = /([^;=]+)=([^;]*)/g;
42
+ let match;
43
+
44
+ while ((match = regex.exec(connectionString)) !== null) {
45
+ params[match[1].trim()] = match[2].trim();
46
+ }
47
+
48
+ return params;
49
+ }
50
+
51
+
52
+ const connect = async (connection: Connection): Promise<void> => {
53
+ return new Promise((resolve, reject) => {
54
+ console.log('Attempting to connect...');
55
+ connection.connect((err) => {
56
+ if (err) {
57
+ reject(err);
58
+ } else {
59
+ resolve();
60
+ }
61
+ });
62
+ });
63
+ }
64
+
65
+ const executeQuery = (connection: Connection, sqlText: string): Promise<any[]> => {
66
+ return new Promise((resolve, reject) => {
67
+ console.log('Executing query...');
68
+ connection.execute({
69
+ sqlText: sqlText,
70
+ complete: (err, stmt, rows) => {
71
+ if (err) {
72
+ reject(err);
73
+ } else {
74
+ console.log('Query executed successfully.');
75
+ if (rows) {
76
+ resolve(rows);
77
+ }
78
+ }
79
+ }
80
+ });
81
+ });
82
+ }
83
+
84
+ const isLogLevel = (value: string): value is LogLevel => {
85
+ return ['ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE'].includes(value as LogLevel)
86
+ }
87
+
88
+ const connectToSnowflake = async (config: Record<string, string>): Promise<Connection> => {
89
+ const logLevel = isLogLevel(config.LOG_LEVEL) ? config.LOG_LEVEL : 'INFO';
90
+ const isDebugMode = config.IS_DEBUG_MODE === 'true';
91
+ snowflake.configure({
92
+ logLevel,
93
+ additionalLogToConsole: isDebugMode,
94
+ });
95
+
96
+ // "SERVER=myaccount.snowflakecomputing.com;UID=myusername;PWD=mypassword;DATABASE=mydatabase;WAREHOUSE=mywarehouse;ROLE=myrole";
97
+ const connection = snowflake.createConnection({
98
+ account: config.SERVER,
99
+ username: config.UID,
100
+ password: config.PWD,
101
+ database: config.DATABASE,
102
+ warehouse: config.WAREHOUSE,
103
+ sfRetryMaxLoginRetries: 3,
104
+ timeout: 10000,
105
+ });
106
+
107
+ try {
108
+ // Connect to Snowflake
109
+ await connect(connection);
110
+
111
+ // Execute the query
112
+ await executeQuery(connection, 'SELECT CURRENT_VERSION();');
113
+
114
+ return connection;
115
+ } catch (err) {
116
+ throw err;
117
+ }
118
+ }
119
+
120
+
121
+ const convertQueryBoolean = (val: string | null) => val === 'YES';
122
+
123
+ const getFieldType = (data_type: string, character_maximum_length: number, numeric_precision: number, numeric_scale: number): string => {
124
+ if (character_maximum_length) {
125
+ return `${data_type}(${character_maximum_length})`;
126
+ }
127
+ if (numeric_precision && numeric_scale) {
128
+ return `${data_type}(${numeric_precision},${numeric_scale})`;
129
+ }
130
+ return data_type;
131
+ };
132
+
133
+ const getDbdefault = (column_default: string, default_type: DefaultType): DefaultInfo => {
134
+ if (default_type === 'string') {
135
+ return {
136
+ type: default_type,
137
+ value: column_default.slice(1, -1),
138
+ };
139
+ }
140
+ return {
141
+ type: default_type,
142
+ value: column_default,
143
+ };
144
+ };
145
+
146
+ const generateField = (row: Record<string, any>): Field => {
147
+ const {
148
+ COLUMN_NAME: column_name,
149
+ DATA_TYPE: data_type,
150
+ CHARACTER_MAXIMUM_LENGTH: character_maximum_length,
151
+ NUMERIC_PRECISION: numeric_precision,
152
+ NUMERIC_SCALE: numeric_scale,
153
+ IDENTITY_INCREMENT: identity_increment,
154
+ IS_NULLABLE: is_nullable,
155
+ COLUMN_DEFAULT: column_default,
156
+ DEFAULT_TYPE: default_type,
157
+ COLUMN_COMMENT: column_comment,
158
+ } = row;
159
+
160
+ const dbdefault = column_default ? getDbdefault(column_default, default_type) : null;
161
+
162
+ const fieldType = {
163
+ type_name: getFieldType(data_type, character_maximum_length, numeric_precision, numeric_scale),
164
+ schemaName: null,
165
+ };
166
+
167
+ return {
168
+ name: column_name,
169
+ type: fieldType,
170
+ dbdefault,
171
+ not_null: !convertQueryBoolean(is_nullable),
172
+ increment: !!identity_increment,
173
+ note: column_comment ? { value: column_comment } : { value: '' },
174
+ };
175
+ };
176
+
177
+ const generateTablesAndFields = async (conn: Connection, schemas: string[]): Promise<{
178
+ tables: Table[],
179
+ fields: FieldsDictionary,
180
+ }> => {
181
+ const fields: FieldsDictionary = {};
182
+ const schemaSql = schemas.length > 0 ? `AND c.table_schema IN (${schemas.map((schema) => `'${schema}'`).join(',')})` : '';
183
+ const tablesAndFieldsSql = `
184
+ SELECT
185
+ c.table_schema,
186
+ c.table_name,
187
+ c.column_name,
188
+ c.data_type,
189
+ c.character_maximum_length,
190
+ c.numeric_precision,
191
+ c.numeric_scale,
192
+ c.is_nullable,
193
+ c.column_default,
194
+ CASE
195
+ WHEN c.column_default IS NULL THEN NULL
196
+ WHEN c.column_default LIKE '''%' THEN 'string'
197
+ WHEN LOWER(c.column_default) = 'true' OR LOWER(c.column_default) = 'false' THEN 'boolean'
198
+ WHEN c.column_default REGEXP '^-?[0-9]+(\\.[0-9]+)?$' THEN 'number'
199
+ ELSE 'expression'
200
+ END AS default_type,
201
+ t.comment AS table_comment,
202
+ c.comment AS column_comment,
203
+ c.identity_increment
204
+ FROM
205
+ information_schema.columns c
206
+ JOIN information_schema.tables t
207
+ ON c.table_name = t.table_name
208
+ AND c.table_schema = t.table_schema
209
+ WHERE
210
+ t.table_type = 'BASE TABLE'
211
+ AND t.table_schema NOT IN ('INFORMATION_SCHEMA')
212
+ ${schemaSql}
213
+ ORDER BY
214
+ c.table_schema,
215
+ c.table_name,
216
+ c.ordinal_position;
217
+ `;
218
+
219
+ const tablesAndFieldsResult = await executeQuery(conn, tablesAndFieldsSql);
220
+ const tables = tablesAndFieldsResult.reduce((acc: Record<string, Table>, row: Record<string, string>) => {
221
+ const { TABLE_SCHEMA: table_schema, TABLE_NAME: table_name, TABLE_COMMENT: table_comment } = row;
222
+ const key = `${table_schema}.${table_name}`;
223
+
224
+ if (!acc[key]) {
225
+ acc[key] = {
226
+ name: table_name,
227
+ schemaName: table_schema,
228
+ note: table_comment ? { value: table_comment } : { value: '' },
229
+ };
230
+ }
231
+
232
+ if (!fields[key]) fields[key] = [];
233
+ const field = generateField(row);
234
+ fields[key].push(field);
235
+
236
+ return acc;
237
+ }, {});
238
+
239
+ return {
240
+ tables: Object.values(tables),
241
+ fields,
242
+ };
243
+ };
244
+
245
+ const createConstraintKeysMap = (keys: Record<string, string>[], schemas: string[], constraintType: 'primary' | 'unique'): Record<string, ConstraintRow> => {
246
+ return keys.reduce((acc: Record<string, ConstraintRow>, row: Record<string, string>) => {
247
+ const { schema_name, table_name, column_name, constraint_name } = row;
248
+ const selectedSchema = schemas.length > 0 ? schemas.includes(schema_name) : true;
249
+ if (!selectedSchema) { return acc; }
250
+
251
+ const key = `${schema_name}.${table_name}.${constraint_name}`;
252
+
253
+ if (acc[key]) {
254
+ const columnNames = acc[key].columnNames;
255
+ columnNames.push(column_name);
256
+ return acc;
257
+ }
258
+
259
+ acc[key] = {
260
+ schemaName: schema_name,
261
+ tableName: table_name,
262
+ constraintName: constraint_name,
263
+ columnNames: [column_name],
264
+ type: '',
265
+ };
266
+ acc[key][constraintType] = true;
267
+
268
+ return acc;
269
+ }, {});
270
+ };
271
+ const generateIndexes = async (conn: Connection, databaseName: string, schemas: string[]) => {
272
+
273
+
274
+ const getPrimaryKeysSql = `
275
+ SHOW PRIMARY KEYS IN DATABASE ${databaseName};
276
+ `;
277
+
278
+ const getUniqueKeysSql = `
279
+ SHOW UNIQUE KEYS IN DATABASE ${databaseName};
280
+ `;
281
+
282
+ const primaryKeys = await executeQuery(conn, getPrimaryKeysSql);
283
+ const uniqueKeys = await executeQuery(conn, getUniqueKeysSql);
284
+
285
+ const primaryKeysByConstraint: Record<string, ConstraintRow> = createConstraintKeysMap(primaryKeys, schemas, 'primary');
286
+ const uniqueKeysByConstraint: Record<string, ConstraintRow> = createConstraintKeysMap(uniqueKeys, schemas, 'unique');
287
+
288
+ const allConstraints: ConstraintRow[] = [Object.values(primaryKeysByConstraint), Object.values(uniqueKeysByConstraint)].flat();
289
+ const { indexes, tableConstraints } = allConstraints.reduce((acc: GeneratedIndexes, row: ConstraintRow): GeneratedIndexes => {
290
+ const { schemaName, tableName, constraintName, columnNames, type, primary, unique } = row;
291
+ const key = `${schemaName}.${tableName}`;
292
+
293
+ if (columnNames.length < 2) {
294
+ acc.tableConstraints[key] = {
295
+ [columnNames[0]]: {
296
+ pk: primary,
297
+ unique,
298
+ },
299
+ };
300
+ return acc;
301
+ }
302
+
303
+ const index: Index = {
304
+ name: constraintName,
305
+ type,
306
+ unique,
307
+ pk: primary,
308
+ columns: columnNames.map((columnName) => ({
309
+ type: 'column',
310
+ value: columnName,
311
+ })),
312
+ };
313
+
314
+ if (acc.indexes[key]) {
315
+ acc.indexes[key].push(index);
316
+ } else {
317
+ acc.indexes[key] = [index];
318
+ }
319
+
320
+ return acc;
321
+ }, { indexes: {}, tableConstraints: {}});
322
+
323
+ return {
324
+ indexes,
325
+ tableConstraints,
326
+ };
327
+ };
328
+
329
+ const fetchSchemaJson = async (connection: string): Promise<DatabaseSchema> => {
330
+ const config = parseConnectionString(connection);
331
+ const conn = await connectToSnowflake(config);
332
+ if (conn instanceof Error) {
333
+ throw conn;
334
+ }
335
+ // Schemas: schema1,schema2,schema3
336
+ const schemas = config.SCHEMAS ? parseSchema(config.SCHEMAS) : [];
337
+ const databaseName = config.DATABASE;
338
+
339
+ const tablesAndFieldsRes = generateTablesAndFields(conn, schemas);
340
+ const indexesRes = generateIndexes(conn, databaseName, schemas);
341
+
342
+ const res = await Promise.all([
343
+ tablesAndFieldsRes,
344
+ indexesRes,
345
+ ]);
346
+
347
+ conn.destroy((err) => {
348
+ if (err) {
349
+ throw err;
350
+ } else {
351
+ console.log('Connection destroyed successfully.');
352
+ }
353
+ });
354
+
355
+ const { tables, fields } = res[0];
356
+ const { indexes, tableConstraints } = res[1];
357
+
358
+ return {
359
+ tables,
360
+ fields,
361
+ refs: [],
362
+ enums: [],
363
+ indexes,
364
+ tableConstraints,
365
+ };
366
+ };
367
+
368
+ export {
369
+ fetchSchemaJson,
370
+ };
@@ -21,6 +21,8 @@ interface Field {
21
21
  dbdefault: DefaultInfo | null;
22
22
  not_null: boolean;
23
23
  increment: boolean;
24
+ pk?: boolean;
25
+ unique?: boolean;
24
26
  note: NoteInfo;
25
27
  }
26
28
 
@@ -95,6 +97,15 @@ interface DatabaseSchema {
95
97
  indexes: IndexesDictionary; // Represents the indexes property
96
98
  }
97
99
 
100
+ interface BigQueryCredentials {
101
+ projectId: string,
102
+ credentials: {
103
+ clientEmail: string,
104
+ privateKey: string,
105
+ },
106
+ datasets: string[],
107
+ }
108
+
98
109
  export {
99
110
  NoteInfo,
100
111
  TypeInfo,
@@ -113,4 +124,5 @@ export {
113
124
  Index,
114
125
  IndexesDictionary,
115
126
  DatabaseSchema,
127
+ BigQueryCredentials,
116
128
  };
@@ -0,0 +1,71 @@
1
+ create schema if not exists dbml_test;
2
+
3
+ create table if not exists dbml_test.test_data_type_default (
4
+ t_int INT64 default 1 OPTIONS(description="Int64 column"),
5
+ t_bool_1 BOOLEAN default true,
6
+ t_bool_2 BOOLEAN default True,
7
+ t_string_1 STRING default 'string column description 1',
8
+ t_string_2 STRING default "string column description 2",
9
+ t_string_3 STRING default "NULL",
10
+ t_float FLOAT64 default 123.67,
11
+ t_decimal DECIMAL default 10,
12
+ t_date DATE default CURRENT_DATE,
13
+ t_datetime DATETIME default CURRENT_DATETIME,
14
+ t_timestamp TIMESTAMP default CURRENT_TIMESTAMP,
15
+ t_array ARRAY<INT64> default ARRAY<INT64>[1, 2, 3],
16
+ t_struct STRUCT<
17
+ street STRING,
18
+ city STRING,
19
+ zip_code INT64
20
+ > default STRUCT('123 Example St', 'Sample City', 12345),
21
+ t_interval INTERVAL,
22
+ t_geo GEOGRAPHY
23
+ ) OPTIONS(description="Table description");
24
+
25
+ create table if not exists dbml_test.test_constraint_1 (
26
+ id int64,
27
+ primary key (id) not enforced
28
+ );
29
+
30
+ create table if not exists dbml_test.test_constraint_2 (
31
+ id int64,
32
+ name string,
33
+
34
+ primary key (id, name) not enforced
35
+ );
36
+
37
+ create table if not exists dbml_test.test_constraint_3 (
38
+ id int64,
39
+ fk_id int64
40
+ );
41
+ alter table dbml_test.test_constraint_3
42
+ add foreign key (fk_id) references dbml_test.test_constraint_1 (id) not enforced;
43
+
44
+ create table if not exists dbml_test.test_constraint_4 (
45
+ fk_id int64,
46
+ fk_name string,
47
+ primary key (fk_id, fk_name) not enforced
48
+ );
49
+ alter table dbml_test.test_constraint_4
50
+ add foreign key (fk_id, fk_name) references dbml_test.test_constraint_2 (id, name) not enforced;
51
+
52
+ create table dbml_test.test_all_index (
53
+ a string,
54
+ b int64,
55
+ c struct <d int64, e array<string>, f struct<g string, h int64>>
56
+ );
57
+
58
+ create search index all_col_index
59
+ on dbml_test.test_all_index(ALL COLUMNS)
60
+ options (analyzer = 'NO_OP_ANALYZER');
61
+
62
+ create table dbml_test.test_multi_index (a string, b int64, c json, d timestamp);
63
+
64
+ create search index multi_index
65
+ on dbml_test.test_multi_index (a, b, c)
66
+ options ( data_types = ['string', 'int64', 'timestamp']);
67
+
68
+ create table dbml_test.test_index (a string, b int64);
69
+
70
+ create search index simple_index
71
+ on dbml_test.test_index (a);