meadow-connection-postgresql 1.0.0 → 1.0.2
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 +133 -0
- package/docker-compose.yml +20 -0
- package/docs/.nojekyll +0 -0
- package/docs/README.md +96 -0
- package/docs/_cover.md +17 -0
- package/docs/_sidebar.md +33 -0
- package/docs/_topbar.md +5 -0
- package/docs/api/connect.md +100 -0
- package/docs/api/connectAsync.md +92 -0
- package/docs/api/createTable.md +116 -0
- package/docs/api/createTables.md +128 -0
- package/docs/api/generateCreateTableStatement.md +136 -0
- package/docs/api/generateDropTableStatement.md +71 -0
- package/docs/api/pool.md +171 -0
- package/docs/api/reference.md +112 -0
- package/docs/api.md +3 -0
- package/docs/architecture.md +168 -0
- package/docs/css/docuserve.css +73 -0
- package/docs/index.html +39 -0
- package/docs/quickstart.md +181 -0
- package/docs/retold-catalog.json +62 -0
- package/docs/retold-keyword-index.json +4964 -0
- package/docs/schema.md +148 -0
- package/package.json +5 -2
- package/source/Meadow-Connection-PostgreSQL.js +79 -99
- package/source/Meadow-Schema-PostgreSQL.js +1048 -0
- package/start-postgresql.sh +21 -0
- package/stop-postgresql.sh +9 -0
- package/test/PostgreSQL_tests.js +865 -1
- package/test/docker-init/01-chinook-schema.sql +177 -0
package/docs/schema.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Schema & Column Types
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The PostgreSQL connector generates standard SQL DDL (`CREATE TABLE`, `DROP TABLE`) from Meadow table schemas. Unlike MongoDB or Dgraph connectors that produce descriptor objects, this connector generates SQL strings that are executed directly via `pool.query()`.
|
|
6
|
+
|
|
7
|
+
## Column Type Mapping
|
|
8
|
+
|
|
9
|
+
| Meadow DataType | PostgreSQL Type | Constraints | Size Used |
|
|
10
|
+
|-----------------|-----------------|-------------|-----------|
|
|
11
|
+
| `ID` | `SERIAL` | `PRIMARY KEY` | No |
|
|
12
|
+
| `GUID` | `VARCHAR(Size)` | `DEFAULT '0xDe'` | Yes (default 36) |
|
|
13
|
+
| `ForeignKey` | `INTEGER` | `NOT NULL DEFAULT 0` | No |
|
|
14
|
+
| `Numeric` | `INTEGER` | `NOT NULL DEFAULT 0` | No |
|
|
15
|
+
| `Decimal` | `DECIMAL(Size)` | -- | Yes (e.g. `10,2`) |
|
|
16
|
+
| `String` | `VARCHAR(Size)` | `NOT NULL DEFAULT ''` | Yes |
|
|
17
|
+
| `Text` | `TEXT` | -- | No |
|
|
18
|
+
| `DateTime` | `TIMESTAMP` | -- | No |
|
|
19
|
+
| `Boolean` | `BOOLEAN` | `NOT NULL DEFAULT false` | No |
|
|
20
|
+
|
|
21
|
+
### Key Behaviors
|
|
22
|
+
|
|
23
|
+
- **`ID`** -- uses PostgreSQL's `SERIAL` type which auto-creates a sequence and sets `NOT NULL DEFAULT nextval(...)`. The `PRIMARY KEY` constraint is declared inline.
|
|
24
|
+
- **`GUID`** -- defaults to `VARCHAR(36)` if no `Size` is provided or if the `Size` value is not a number.
|
|
25
|
+
- **`Boolean`** -- uses PostgreSQL's native `BOOLEAN` type (not `TINYINT` like MySQL).
|
|
26
|
+
- **`Decimal`** -- the `Size` property is passed directly (e.g. `'10,2'` becomes `DECIMAL(10,2)`).
|
|
27
|
+
- **Quoted identifiers** -- all table and column names are wrapped in double quotes (`"Name"`) to safely handle PostgreSQL reserved words.
|
|
28
|
+
|
|
29
|
+
## Generated DDL Example
|
|
30
|
+
|
|
31
|
+
Given this Meadow table schema:
|
|
32
|
+
|
|
33
|
+
```javascript
|
|
34
|
+
let tmpAnimalSchema =
|
|
35
|
+
{
|
|
36
|
+
TableName: 'Animal',
|
|
37
|
+
Columns:
|
|
38
|
+
[
|
|
39
|
+
{ Column: 'IDAnimal', DataType: 'ID' },
|
|
40
|
+
{ Column: 'GUIDAnimal', DataType: 'GUID', Size: 36 },
|
|
41
|
+
{ Column: 'Name', DataType: 'String', Size: 128 },
|
|
42
|
+
{ Column: 'Age', DataType: 'Numeric' },
|
|
43
|
+
{ Column: 'Weight', DataType: 'Decimal', Size: '10,2' },
|
|
44
|
+
{ Column: 'Description', DataType: 'Text' },
|
|
45
|
+
{ Column: 'CreateDate', DataType: 'DateTime' },
|
|
46
|
+
{ Column: 'Deleted', DataType: 'Boolean' }
|
|
47
|
+
]
|
|
48
|
+
};
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The generated DDL:
|
|
52
|
+
|
|
53
|
+
```sql
|
|
54
|
+
-- [ Animal ]
|
|
55
|
+
CREATE TABLE IF NOT EXISTS
|
|
56
|
+
"Animal"
|
|
57
|
+
(
|
|
58
|
+
"IDAnimal" SERIAL PRIMARY KEY,
|
|
59
|
+
"GUIDAnimal" VARCHAR(36) DEFAULT '0xDe',
|
|
60
|
+
"Name" VARCHAR(128) NOT NULL DEFAULT '',
|
|
61
|
+
"Age" INTEGER NOT NULL DEFAULT 0,
|
|
62
|
+
"Weight" DECIMAL(10,2),
|
|
63
|
+
"Description" TEXT,
|
|
64
|
+
"CreateDate" TIMESTAMP,
|
|
65
|
+
"Deleted" BOOLEAN NOT NULL DEFAULT false
|
|
66
|
+
);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Drop Table Statement
|
|
70
|
+
|
|
71
|
+
The `generateDropTableStatement(name)` method generates a `DROP TABLE IF EXISTS` statement:
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
let tmpDrop = _Fable.MeadowPostgreSQLProvider.generateDropTableStatement('Animal');
|
|
75
|
+
// => 'DROP TABLE IF EXISTS "Animal";'
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Multiple Tables
|
|
79
|
+
|
|
80
|
+
Use `createTables()` to create multiple tables from a Stricture schema:
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
let tmpSchema =
|
|
84
|
+
{
|
|
85
|
+
Tables:
|
|
86
|
+
[
|
|
87
|
+
{
|
|
88
|
+
TableName: 'Animal',
|
|
89
|
+
Columns:
|
|
90
|
+
[
|
|
91
|
+
{ Column: 'IDAnimal', DataType: 'ID' },
|
|
92
|
+
{ Column: 'GUIDAnimal', DataType: 'GUID', Size: 36 },
|
|
93
|
+
{ Column: 'Name', DataType: 'String', Size: 128 }
|
|
94
|
+
]
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
TableName: 'Farm',
|
|
98
|
+
Columns:
|
|
99
|
+
[
|
|
100
|
+
{ Column: 'IDFarm', DataType: 'ID' },
|
|
101
|
+
{ Column: 'GUIDFarm', DataType: 'GUID', Size: 36 },
|
|
102
|
+
{ Column: 'FarmName', DataType: 'String', Size: 256 }
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
_Fable.MeadowPostgreSQLProvider.createTables(tmpSchema,
|
|
109
|
+
(pError) =>
|
|
110
|
+
{
|
|
111
|
+
if (pError) { console.error(pError); return; }
|
|
112
|
+
console.log('All tables created!');
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Tables are created sequentially (concurrency of 1) to ensure deterministic ordering.
|
|
117
|
+
|
|
118
|
+
## PostgreSQL vs MySQL Type Mapping
|
|
119
|
+
|
|
120
|
+
| Meadow DataType | PostgreSQL | MySQL |
|
|
121
|
+
|-----------------|-----------|-------|
|
|
122
|
+
| `ID` | `SERIAL PRIMARY KEY` | `INT UNSIGNED NOT NULL AUTO_INCREMENT` |
|
|
123
|
+
| `GUID` | `VARCHAR(36)` | `CHAR(36)` |
|
|
124
|
+
| `ForeignKey` | `INTEGER NOT NULL DEFAULT 0` | `INT UNSIGNED NOT NULL DEFAULT 0` |
|
|
125
|
+
| `Numeric` | `INTEGER NOT NULL DEFAULT 0` | `INT NOT NULL DEFAULT 0` |
|
|
126
|
+
| `Decimal` | `DECIMAL(Size)` | `DECIMAL(Size)` |
|
|
127
|
+
| `String` | `VARCHAR(Size) NOT NULL DEFAULT ''` | `VARCHAR(Size) NOT NULL DEFAULT ''` |
|
|
128
|
+
| `Text` | `TEXT` | `TEXT` |
|
|
129
|
+
| `DateTime` | `TIMESTAMP` | `DATETIME` |
|
|
130
|
+
| `Boolean` | `BOOLEAN NOT NULL DEFAULT false` | `TINYINT NOT NULL DEFAULT 0` |
|
|
131
|
+
|
|
132
|
+
### Notable Differences
|
|
133
|
+
|
|
134
|
+
- **Auto-increment**: PostgreSQL uses `SERIAL` (sequence-backed); MySQL uses `AUTO_INCREMENT`
|
|
135
|
+
- **Boolean**: PostgreSQL has a native `BOOLEAN` type; MySQL uses `TINYINT`
|
|
136
|
+
- **DateTime**: PostgreSQL uses `TIMESTAMP`; MySQL uses `DATETIME`
|
|
137
|
+
- **Integer signing**: PostgreSQL integers are always signed; MySQL supports `UNSIGNED`
|
|
138
|
+
- **GUID default**: PostgreSQL uses `DEFAULT '0xDe'`; this placeholder is replaced at insertion time by Meadow's auto-GUID mechanism
|
|
139
|
+
- **Identifiers**: PostgreSQL uses double quotes (`"Name"`); MySQL uses backticks (`` `Name` ``)
|
|
140
|
+
|
|
141
|
+
## Idempotent Schema
|
|
142
|
+
|
|
143
|
+
Table creation is idempotent through two mechanisms:
|
|
144
|
+
|
|
145
|
+
1. **`CREATE TABLE IF NOT EXISTS`** -- PostgreSQL skips creation if the table exists
|
|
146
|
+
2. **Error code 42P07** -- the connector catches `duplicate_table` errors and logs a warning instead of failing
|
|
147
|
+
|
|
148
|
+
This makes `createTable()` and `createTables()` safe to call during application startup.
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "meadow-connection-postgresql",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Meadow PostgreSQL Connection Plugin",
|
|
5
5
|
"main": "source/Meadow-Connection-PostgreSQL.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"coverage": "npx quack coverage",
|
|
8
|
-
"test": "npx quack test"
|
|
8
|
+
"test": "npx quack test",
|
|
9
|
+
"docker-postgresql-start": "bash ./start-postgresql.sh",
|
|
10
|
+
"docker-postgresql-stop": "bash ./stop-postgresql.sh",
|
|
11
|
+
"test-docker": "bash ./start-postgresql.sh && npm test; bash ./stop-postgresql.sh"
|
|
9
12
|
},
|
|
10
13
|
"mocha": {
|
|
11
14
|
"diff": true,
|
|
@@ -6,6 +6,8 @@ const libFableServiceProviderBase = require('fable-serviceproviderbase');
|
|
|
6
6
|
|
|
7
7
|
const libPG = require('pg');
|
|
8
8
|
|
|
9
|
+
const libMeadowSchemaPostgreSQL = require('./Meadow-Schema-PostgreSQL.js');
|
|
10
|
+
|
|
9
11
|
class MeadowConnectionPostgreSQL extends libFableServiceProviderBase
|
|
10
12
|
{
|
|
11
13
|
constructor(pFable, pManifest, pServiceHash)
|
|
@@ -66,128 +68,105 @@ class MeadowConnectionPostgreSQL extends libFableServiceProviderBase
|
|
|
66
68
|
this._ConnectionPool = false;
|
|
67
69
|
this.connected = false;
|
|
68
70
|
|
|
71
|
+
// Schema provider handles DDL operations (create, drop, index, etc.)
|
|
72
|
+
this._SchemaProvider = new libMeadowSchemaPostgreSQL(this.fable, this.options, `${this.Hash}-Schema`);
|
|
73
|
+
|
|
69
74
|
if (this.options.MeadowConnectionPostgreSQLAutoConnect)
|
|
70
75
|
{
|
|
71
76
|
this.connect();
|
|
72
77
|
}
|
|
73
78
|
}
|
|
74
79
|
|
|
80
|
+
get schemaProvider()
|
|
81
|
+
{
|
|
82
|
+
return this._SchemaProvider;
|
|
83
|
+
}
|
|
84
|
+
|
|
75
85
|
generateDropTableStatement(pTableName)
|
|
76
86
|
{
|
|
77
|
-
return
|
|
87
|
+
return this._SchemaProvider.generateDropTableStatement(pTableName);
|
|
78
88
|
}
|
|
79
89
|
|
|
80
90
|
generateCreateTableStatement(pMeadowTableSchema)
|
|
81
91
|
{
|
|
82
|
-
this.
|
|
92
|
+
return this._SchemaProvider.generateCreateTableStatement(pMeadowTableSchema);
|
|
93
|
+
}
|
|
83
94
|
|
|
84
|
-
|
|
85
|
-
|
|
95
|
+
createTables(pMeadowSchema, fCallback)
|
|
96
|
+
{
|
|
97
|
+
return this._SchemaProvider.createTables(pMeadowSchema, fCallback);
|
|
98
|
+
}
|
|
86
99
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
100
|
+
createTable(pMeadowTableSchema, fCallback)
|
|
101
|
+
{
|
|
102
|
+
return this._SchemaProvider.createTable(pMeadowTableSchema, fCallback);
|
|
103
|
+
}
|
|
91
104
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
105
|
+
getIndexDefinitionsFromSchema(pMeadowTableSchema)
|
|
106
|
+
{
|
|
107
|
+
return this._SchemaProvider.getIndexDefinitionsFromSchema(pMeadowTableSchema);
|
|
108
|
+
}
|
|
97
109
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
case 'ID':
|
|
103
|
-
tmpCreateTableStatement += ` "${tmpColumn.Column}" SERIAL PRIMARY KEY`;
|
|
104
|
-
tmpPrimaryKey = tmpColumn.Column;
|
|
105
|
-
break;
|
|
106
|
-
case 'GUID':
|
|
107
|
-
let tmpSize = tmpColumn.hasOwnProperty('Size') ? tmpColumn.Size : 36;
|
|
108
|
-
if (isNaN(tmpSize))
|
|
109
|
-
{
|
|
110
|
-
// Use the old default if Size is improper
|
|
111
|
-
tmpSize = 36;
|
|
112
|
-
}
|
|
113
|
-
tmpCreateTableStatement += ` "${tmpColumn.Column}" VARCHAR(${tmpSize}) DEFAULT '0xDe'`;
|
|
114
|
-
break;
|
|
115
|
-
case 'ForeignKey':
|
|
116
|
-
tmpCreateTableStatement += ` "${tmpColumn.Column}" INTEGER NOT NULL DEFAULT 0`;
|
|
117
|
-
break;
|
|
118
|
-
case 'Numeric':
|
|
119
|
-
tmpCreateTableStatement += ` "${tmpColumn.Column}" INTEGER NOT NULL DEFAULT 0`;
|
|
120
|
-
break;
|
|
121
|
-
case 'Decimal':
|
|
122
|
-
tmpCreateTableStatement += ` "${tmpColumn.Column}" DECIMAL(${tmpColumn.Size})`;
|
|
123
|
-
break;
|
|
124
|
-
case 'String':
|
|
125
|
-
tmpCreateTableStatement += ` "${tmpColumn.Column}" VARCHAR(${tmpColumn.Size}) NOT NULL DEFAULT ''`;
|
|
126
|
-
break;
|
|
127
|
-
case 'Text':
|
|
128
|
-
tmpCreateTableStatement += ` "${tmpColumn.Column}" TEXT`;
|
|
129
|
-
break;
|
|
130
|
-
case 'DateTime':
|
|
131
|
-
tmpCreateTableStatement += ` "${tmpColumn.Column}" TIMESTAMP`;
|
|
132
|
-
break;
|
|
133
|
-
case 'Boolean':
|
|
134
|
-
tmpCreateTableStatement += ` "${tmpColumn.Column}" BOOLEAN NOT NULL DEFAULT false`;
|
|
135
|
-
break;
|
|
136
|
-
default:
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
// PostgreSQL SERIAL PRIMARY KEY is declared inline; no separate PRIMARY KEY clause needed
|
|
141
|
-
tmpCreateTableStatement += `\n );`;
|
|
110
|
+
generateCreateIndexScript(pMeadowTableSchema)
|
|
111
|
+
{
|
|
112
|
+
return this._SchemaProvider.generateCreateIndexScript(pMeadowTableSchema);
|
|
113
|
+
}
|
|
142
114
|
|
|
143
|
-
|
|
115
|
+
generateCreateIndexStatements(pMeadowTableSchema)
|
|
116
|
+
{
|
|
117
|
+
return this._SchemaProvider.generateCreateIndexStatements(pMeadowTableSchema);
|
|
144
118
|
}
|
|
145
119
|
|
|
146
|
-
|
|
120
|
+
createIndex(pIndexStatement, fCallback)
|
|
147
121
|
{
|
|
148
|
-
this.
|
|
149
|
-
(pTable, fCreateComplete) =>
|
|
150
|
-
{
|
|
151
|
-
return this.createTable(pTable, fCreateComplete);
|
|
152
|
-
},
|
|
153
|
-
(pCreateError) =>
|
|
154
|
-
{
|
|
155
|
-
if (pCreateError)
|
|
156
|
-
{
|
|
157
|
-
this.log.error(`Meadow-PostgreSQL Error creating tables from Schema: ${pCreateError}`, pCreateError);
|
|
158
|
-
}
|
|
159
|
-
this.log.info('Done creating tables!');
|
|
160
|
-
return fCallback(pCreateError);
|
|
161
|
-
});
|
|
122
|
+
return this._SchemaProvider.createIndex(pIndexStatement, fCallback);
|
|
162
123
|
}
|
|
163
124
|
|
|
164
|
-
|
|
125
|
+
createIndices(pMeadowTableSchema, fCallback)
|
|
165
126
|
{
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
127
|
+
return this._SchemaProvider.createIndices(pMeadowTableSchema, fCallback);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
createAllIndices(pMeadowSchema, fCallback)
|
|
131
|
+
{
|
|
132
|
+
return this._SchemaProvider.createAllIndices(pMeadowSchema, fCallback);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Database Introspection delegation
|
|
136
|
+
|
|
137
|
+
listTables(fCallback)
|
|
138
|
+
{
|
|
139
|
+
return this._SchemaProvider.listTables(fCallback);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
introspectTableColumns(pTableName, fCallback)
|
|
143
|
+
{
|
|
144
|
+
return this._SchemaProvider.introspectTableColumns(pTableName, fCallback);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
introspectTableIndices(pTableName, fCallback)
|
|
148
|
+
{
|
|
149
|
+
return this._SchemaProvider.introspectTableIndices(pTableName, fCallback);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
introspectTableForeignKeys(pTableName, fCallback)
|
|
153
|
+
{
|
|
154
|
+
return this._SchemaProvider.introspectTableForeignKeys(pTableName, fCallback);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
introspectTableSchema(pTableName, fCallback)
|
|
158
|
+
{
|
|
159
|
+
return this._SchemaProvider.introspectTableSchema(pTableName, fCallback);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
introspectDatabaseSchema(fCallback)
|
|
163
|
+
{
|
|
164
|
+
return this._SchemaProvider.introspectDatabaseSchema(fCallback);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
generateMeadowPackageFromTable(pTableName, fCallback)
|
|
168
|
+
{
|
|
169
|
+
return this._SchemaProvider.generateMeadowPackageFromTable(pTableName, fCallback);
|
|
191
170
|
}
|
|
192
171
|
|
|
193
172
|
connect()
|
|
@@ -212,6 +191,7 @@ class MeadowConnectionPostgreSQL extends libFableServiceProviderBase
|
|
|
212
191
|
{
|
|
213
192
|
this.fable.log.info(`Meadow-Connection-PostgreSQL connecting to [${this.options.PostgreSQL.host} : ${this.options.PostgreSQL.port}] as ${this.options.PostgreSQL.user} for database ${this.options.PostgreSQL.database} at a pool limit of ${this.options.PostgreSQL.max}`);
|
|
214
193
|
this._ConnectionPool = new libPG.Pool(tmpConnectionSettings);
|
|
194
|
+
this._SchemaProvider.setConnectionPool(this._ConnectionPool);
|
|
215
195
|
this.connected = true;
|
|
216
196
|
}
|
|
217
197
|
}
|