@travetto/model-postgres 7.0.0-rc.2 → 7.0.0-rc.3

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.
package/README.md CHANGED
@@ -74,9 +74,9 @@ export class SQLModelConfig<T extends {} = {}> {
74
74
  */
75
75
  database = 'app';
76
76
  /**
77
- * Auto schema creation
77
+ * Allow storage modification at runtime
78
78
  */
79
- autoCreate?: boolean;
79
+ modifyStorage?: boolean;
80
80
  /**
81
81
  * Db version
82
82
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/model-postgres",
3
- "version": "7.0.0-rc.2",
3
+ "version": "7.0.0-rc.3",
4
4
  "description": "PostgreSQL backing for the travetto model module, with real-time modeling support for SQL schemas.",
5
5
  "keywords": [
6
6
  "sql",
@@ -27,12 +27,12 @@
27
27
  "directory": "module/model-postgres"
28
28
  },
29
29
  "dependencies": {
30
- "@travetto/cli": "^7.0.0-rc.2",
31
- "@travetto/config": "^7.0.0-rc.2",
32
- "@travetto/context": "^7.0.0-rc.2",
33
- "@travetto/model": "^7.0.0-rc.2",
34
- "@travetto/model-query": "^7.0.0-rc.2",
35
- "@travetto/model-sql": "^7.0.0-rc.2",
30
+ "@travetto/cli": "^7.0.0-rc.3",
31
+ "@travetto/config": "^7.0.0-rc.3",
32
+ "@travetto/context": "^7.0.0-rc.3",
33
+ "@travetto/model": "^7.0.0-rc.3",
34
+ "@travetto/model-query": "^7.0.0-rc.3",
35
+ "@travetto/model-sql": "^7.0.0-rc.3",
36
36
  "@types/pg": "^8.15.6",
37
37
  "pg": "^8.16.3"
38
38
  },
package/src/connection.ts CHANGED
@@ -59,10 +59,13 @@ export class PostgreSQLConnection extends Connection<PoolClient> {
59
59
  const records: T[] = [...out.rows].map(value => ({ ...value }));
60
60
  return { count: out.rowCount!, records };
61
61
  } catch (error) {
62
- if (error instanceof Error && error.message.includes('duplicate key value')) {
63
- throw new ExistsError('query', query);
64
- } else {
65
- throw error;
62
+ const code = error && typeof error === 'object' && 'code' in error ? error.code : undefined;
63
+ switch (code) {
64
+ // Index already exists
65
+ case '42P07': throw new ExistsError('index', query);
66
+ // Unique violation
67
+ case '23505': throw new ExistsError('query', query);
68
+ default: throw error;
66
69
  }
67
70
  }
68
71
  }
package/src/dialect.ts CHANGED
@@ -4,7 +4,7 @@ import { AsyncContext } from '@travetto/context';
4
4
  import { ModelType } from '@travetto/model';
5
5
  import { castTo, Class } from '@travetto/runtime';
6
6
 
7
- import { SQLDialect, SQLModelConfig, SQLModelUtil, VisitStack } from '@travetto/model-sql';
7
+ import { SQLDialect, SQLModelConfig, SQLModelUtil, VisitStack, type SQLTableDescription } from '@travetto/model-sql';
8
8
 
9
9
  import { PostgreSQLConnection } from './connection.ts';
10
10
 
@@ -44,6 +44,90 @@ export class PostgreSQLDialect extends SQLDialect {
44
44
  return `encode(digest('${value}', 'sha1'), 'hex')`;
45
45
  }
46
46
 
47
+ async describeTable(table: string): Promise<SQLTableDescription | undefined> {
48
+ const IGNORE_FIELDS = [this.pathField.name, this.parentPathField.name, this.idxField.name].map(field => `'${field}'`);
49
+
50
+ const [columns, foreignKeys, indices] = await Promise.all([
51
+ // 1. Columns
52
+ this.executeSQL<{ name: string, type: string, is_notnull: boolean }>(`
53
+ SELECT
54
+ a.attname AS name,
55
+ pg_catalog.format_type(a.atttypid, a.atttypmod) AS type,
56
+ a.attnotnull AS is_notnull
57
+ FROM pg_catalog.pg_attribute a
58
+ JOIN pg_catalog.pg_class c ON c.oid = a.attrelid
59
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
60
+ LEFT JOIN
61
+ pg_catalog.pg_attrdef ad ON ad.adrelid = c.oid AND ad.adnum = a.attnum
62
+ WHERE
63
+ c.relname = '${table}'
64
+ AND a.attnum > 0
65
+ AND NOT a.attisdropped
66
+ AND a.attname NOT IN (${IGNORE_FIELDS.join(',')})
67
+ ORDER BY
68
+ a.attnum;
69
+ `),
70
+
71
+ // 2. Foreign Keys
72
+ this.executeSQL<{ name: string, from_column: string, to_column: string, to_table: string }>(`
73
+ SELECT
74
+ tc.constraint_name AS name,
75
+ kcu.column_name AS from_column,
76
+ ccu.column_name AS to_column,
77
+ ccu.table_name AS to_table
78
+ FROM information_schema.table_constraints AS tc
79
+ JOIN information_schema.key_column_usage AS kcu
80
+ ON tc.constraint_name = kcu.constraint_name
81
+ JOIN information_schema.constraint_column_usage AS ccu
82
+ ON ccu.constraint_name = tc.constraint_name
83
+ WHERE tc.constraint_type = 'FOREIGN KEY'
84
+ AND tc.table_name = '${table}'
85
+ `),
86
+
87
+ // 3. Indices
88
+ this.executeSQL<{ name: string, is_unique: boolean, columns: string[] }>(`
89
+ SELECT
90
+ i.relname AS name,
91
+ ix.indisunique AS is_unique,
92
+ ARRAY_AGG(a.attname || ' '|| CAST((o.OPTION & 1) AS VARCHAR) ORDER BY array_position(ix.indkey, a.attnum)) AS columns
93
+ FROM pg_class t
94
+ JOIN pg_index ix ON t.oid = ix.indrelid
95
+ JOIN pg_class i ON i.oid = ix.indexrelid
96
+ CROSS JOIN LATERAL UNNEST(ix.indkey) WITH ordinality AS c (colnum, ordinality)
97
+ LEFT JOIN LATERAL UNNEST(ix.indoption) WITH ordinality AS o (OPTION, ordinality) ON c.ordinality = o.ordinality
98
+ LEFT JOIN pg_catalog.pg_constraint co ON co.conindid = ix.indexrelid
99
+ JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = c.colnum
100
+ WHERE t.relname = '${table}'
101
+ AND NOT ix.indisprimary
102
+ AND co.conindid IS NULL
103
+ GROUP BY i.relname, ix.indisunique
104
+ `)
105
+ ]);
106
+
107
+ if (!columns.count) {
108
+ return undefined;
109
+ }
110
+
111
+ return {
112
+ columns: columns.records.map(col => ({
113
+ ...col,
114
+ type: col.type.toUpperCase()
115
+ .replace('CHARACTER VARYING', 'VARCHAR')
116
+ .replace('INTEGER', 'INT'),
117
+ is_notnull: !!col.is_notnull
118
+ })),
119
+ foreignKeys: foreignKeys.records,
120
+ indices: indices.records
121
+ .map(idx => ({
122
+ name: idx.name,
123
+ is_unique: idx.is_unique,
124
+ columns: idx.columns
125
+ .map(column => column.split(' '))
126
+ .map(([name, desc]) => ({ name, desc: desc === '1' }))
127
+ }))
128
+ };
129
+ }
130
+
47
131
  /**
48
132
  * Define column modification
49
133
  */
@@ -8,8 +8,8 @@ export const service: ServiceDescriptor = {
8
8
  port: 5432,
9
9
  image: `postgres:${version}-alpine`,
10
10
  env: {
11
- POSTGRES_USER: 'root',
12
- POSTGRES_PASSWORD: 'password',
11
+ POSTGRES_USER: 'travetto',
12
+ POSTGRES_PASSWORD: 'travetto',
13
13
  POSTGRES_DB: 'app'
14
14
  }
15
15
  };