database-model-generator 0.6.0 → 0.7.0
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGES.md +12 -0
- data/MANIFEST.md +32 -4
- data/README.md +25 -8
- data/bin/dmg +97 -33
- data/database-model-generator.gemspec +8 -3
- data/docker/postgresql/Dockerfile +22 -0
- data/docker/postgresql/POSTGRESQL.md +278 -0
- data/docker/postgresql/README.md +116 -0
- data/docker/postgresql/docker-compose.yml +51 -0
- data/docker/postgresql/init-db.sql +101 -0
- data/docker/postgresql/test-Dockerfile +20 -0
- data/docker/postgresql/test.sh +18 -0
- data/lib/database_model_generator.rb +12 -1
- data/lib/postgresql/model/generator.rb +304 -0
- data/spec/oracle_model_generator_spec.rb +1 -1
- data/spec/postgresql_model_generator_spec.rb +325 -0
- data/spec/support/postgresql_connection.rb +71 -0
- data.tar.gz.sig +0 -0
- metadata +45 -8
- metadata.gz.sig +0 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# PostgreSQL Support
|
|
2
|
+
|
|
3
|
+
This document describes the PostgreSQL implementation for the database-model-generator library.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
PostgreSQL support has been added to the database-model-generator library, allowing you to generate ActiveRecord models from existing PostgreSQL tables and views. The implementation follows the same architecture as Oracle and SQL Server support.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
The PostgreSQL generator includes:
|
|
12
|
+
|
|
13
|
+
- **Full table introspection** via PostgreSQL's `information_schema`
|
|
14
|
+
- **Primary key detection** using `pg_index` and `pg_attribute`
|
|
15
|
+
- **Foreign key detection** with automatic relationship inference
|
|
16
|
+
- **Constraint detection** including CHECK, UNIQUE, and NOT NULL constraints
|
|
17
|
+
- **Enum detection** from CHECK constraints with IN clauses
|
|
18
|
+
- **Polymorphic association detection** (e.g., commentable_id + commentable_type)
|
|
19
|
+
- **Index recommendations** for foreign keys, dates, text search, and composite indexes
|
|
20
|
+
- **Full-text search support** using PostgreSQL's GIN indexes and tsvector
|
|
21
|
+
- **Dependency tracking** for views and related objects
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
Add the `pg` gem to your Gemfile or install it directly:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
gem install pg
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
### With the Command Line Tool
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Using explicit database type
|
|
37
|
+
dmg -T postgresql -h localhost -d your_database -t users -u postgres [-p password]
|
|
38
|
+
|
|
39
|
+
# Using auto-detection (if connection parameters indicate PostgreSQL)
|
|
40
|
+
dmg -h localhost -d your_database -t users -u postgres [-p password]
|
|
41
|
+
|
|
42
|
+
# Password is optional if pg_hba.conf is configured for trust authentication
|
|
43
|
+
dmg -T postgresql -h localhost -d your_database -t users -u postgres
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Programmatically
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
require 'pg'
|
|
50
|
+
require 'database_model_generator'
|
|
51
|
+
|
|
52
|
+
# Connect to PostgreSQL
|
|
53
|
+
# Note: password is optional if pg_hba.conf allows trust authentication
|
|
54
|
+
conn = PG.connect(
|
|
55
|
+
host: 'localhost',
|
|
56
|
+
port: 5432,
|
|
57
|
+
dbname: 'your_database',
|
|
58
|
+
user: 'postgres'
|
|
59
|
+
# password: 'your_password' # Only needed if authentication is required
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Create a generator instance
|
|
63
|
+
generator = DatabaseModel::Generator.new(conn)
|
|
64
|
+
|
|
65
|
+
# Generate model information
|
|
66
|
+
generator.generate('users')
|
|
67
|
+
|
|
68
|
+
# Access model information
|
|
69
|
+
puts "Model name: #{generator.model}"
|
|
70
|
+
puts "Table name: #{generator.table}"
|
|
71
|
+
puts "Columns: #{generator.column_names.join(', ')}"
|
|
72
|
+
puts "Primary keys: #{generator.primary_keys.join(', ')}"
|
|
73
|
+
puts "Foreign keys: #{generator.foreign_keys.join(', ')}"
|
|
74
|
+
|
|
75
|
+
# Get enum columns
|
|
76
|
+
if generator.has_enum_columns?
|
|
77
|
+
puts "\nEnum columns detected:"
|
|
78
|
+
generator.enum_columns.each do |enum_col|
|
|
79
|
+
puts " #{enum_col[:name]}: #{enum_col[:values].join(', ')}"
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Get polymorphic associations
|
|
84
|
+
if generator.has_polymorphic_associations?
|
|
85
|
+
puts "\nPolymorphic associations:"
|
|
86
|
+
generator.polymorphic_associations.each do |assoc|
|
|
87
|
+
puts " #{assoc[:name]} (#{assoc[:foreign_key]}, #{assoc[:foreign_type]})"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Get index recommendations
|
|
92
|
+
recommendations = generator.index_recommendations
|
|
93
|
+
puts "\nIndex recommendations:"
|
|
94
|
+
recommendations[:foreign_keys].each do |rec|
|
|
95
|
+
puts " #{rec[:sql]}"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Close connection
|
|
99
|
+
generator.disconnect
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## PostgreSQL-Specific Features
|
|
103
|
+
|
|
104
|
+
### Full-Text Search
|
|
105
|
+
|
|
106
|
+
PostgreSQL's powerful full-text search is supported through GIN indexes:
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
generator.generate('posts')
|
|
110
|
+
recommendations = generator.index_recommendations
|
|
111
|
+
|
|
112
|
+
# Full-text recommendations use to_tsvector
|
|
113
|
+
recommendations[:full_text].each do |rec|
|
|
114
|
+
puts rec[:sql]
|
|
115
|
+
# CREATE INDEX idx_posts_content_text ON posts
|
|
116
|
+
# USING GIN (to_tsvector('english', content))
|
|
117
|
+
end
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Enum Detection from CHECK Constraints
|
|
121
|
+
|
|
122
|
+
PostgreSQL CHECK constraints are parsed to detect enum columns:
|
|
123
|
+
|
|
124
|
+
```sql
|
|
125
|
+
CREATE TABLE users (
|
|
126
|
+
status VARCHAR(20) DEFAULT 'active',
|
|
127
|
+
CONSTRAINT check_status CHECK (status IN ('active', 'inactive', 'suspended'))
|
|
128
|
+
);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
generator.generate('users')
|
|
133
|
+
generator.enum_columns
|
|
134
|
+
# => [{name: 'status', values: ['active', 'inactive', 'suspended'], ...}]
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Data Type Support
|
|
138
|
+
|
|
139
|
+
The PostgreSQL generator handles all standard PostgreSQL data types:
|
|
140
|
+
|
|
141
|
+
- **Character types**: CHAR, VARCHAR, TEXT, CHARACTER VARYING
|
|
142
|
+
- **Numeric types**: INTEGER, BIGINT, DECIMAL, NUMERIC, REAL, DOUBLE PRECISION
|
|
143
|
+
- **Date/Time types**: DATE, TIME, TIMESTAMP, TIMESTAMPTZ, INTERVAL
|
|
144
|
+
- **Boolean type**: BOOLEAN
|
|
145
|
+
- **Binary types**: BYTEA
|
|
146
|
+
- **JSON types**: JSON, JSONB
|
|
147
|
+
- **Arrays and custom types**: Detected via `udt_name` column
|
|
148
|
+
|
|
149
|
+
## Testing
|
|
150
|
+
|
|
151
|
+
### Using Docker
|
|
152
|
+
|
|
153
|
+
The easiest way to test PostgreSQL support is using the provided Docker setup:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
cd docker/postgresql
|
|
157
|
+
|
|
158
|
+
# Start PostgreSQL
|
|
159
|
+
docker-compose up -d postgres-db
|
|
160
|
+
|
|
161
|
+
# Wait for it to be healthy
|
|
162
|
+
docker-compose ps
|
|
163
|
+
|
|
164
|
+
# Run tests
|
|
165
|
+
docker-compose run --rm dmg_test
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Manual Testing
|
|
169
|
+
|
|
170
|
+
If you have PostgreSQL installed locally:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
# Create test database
|
|
174
|
+
createdb test_db
|
|
175
|
+
|
|
176
|
+
# Run initialization script
|
|
177
|
+
psql -d test_db -f docker/postgresql/init-db.sql
|
|
178
|
+
|
|
179
|
+
# Run tests
|
|
180
|
+
bundle exec rspec spec/postgresql_model_generator_spec.rb
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Database Schema Example
|
|
184
|
+
|
|
185
|
+
The test database includes:
|
|
186
|
+
|
|
187
|
+
- **users** - with status and role enum columns
|
|
188
|
+
- **posts** - with foreign key to users, status and priority enums
|
|
189
|
+
- **comments** - with polymorphic association (commentable)
|
|
190
|
+
- **tags** - simple lookup table
|
|
191
|
+
- **post_tags** - many-to-many join table with composite primary key
|
|
192
|
+
|
|
193
|
+
## Architecture
|
|
194
|
+
|
|
195
|
+
The PostgreSQL generator is implemented in `lib/postgresql/model/generator.rb` and extends the `DatabaseModel::Generator::Base` class, providing PostgreSQL-specific implementations for:
|
|
196
|
+
|
|
197
|
+
- `validate_connection` - ensures connection is a `PG::Connection`
|
|
198
|
+
- `normalize_table_name` - converts to lowercase (PostgreSQL convention)
|
|
199
|
+
- `check_table_exists` - queries `information_schema.tables`
|
|
200
|
+
- `get_column_info` - retrieves column metadata
|
|
201
|
+
- `get_primary_keys` - queries `pg_index` system catalog
|
|
202
|
+
- `get_foreign_keys` - queries `information_schema.table_constraints`
|
|
203
|
+
- `get_constraints` - retrieves all constraint information
|
|
204
|
+
- `get_dependencies` - finds dependent views and objects
|
|
205
|
+
- Type checking methods for PostgreSQL data types
|
|
206
|
+
|
|
207
|
+
## Comparison with Other Databases
|
|
208
|
+
|
|
209
|
+
| Feature | Oracle | SQL Server | PostgreSQL |
|
|
210
|
+
|---------|--------|------------|------------|
|
|
211
|
+
| Connection | OCI8 | TinyTds::Client | PG::Connection |
|
|
212
|
+
| Schema queries | USER_* views | INFORMATION_SCHEMA | INFORMATION_SCHEMA + pg_* |
|
|
213
|
+
| Table name case | UPPERCASE | Case-sensitive | lowercase |
|
|
214
|
+
| Full-text search | Oracle Text | FULLTEXT INDEX | GIN + tsvector |
|
|
215
|
+
| Enum detection | ✓ | ✓ | ✓ |
|
|
216
|
+
| Polymorphic | ✓ | ✓ | ✓ |
|
|
217
|
+
| Composite PKs | ✓ | ✓ | ✓ |
|
|
218
|
+
|
|
219
|
+
## Troubleshooting
|
|
220
|
+
|
|
221
|
+
### Connection Issues
|
|
222
|
+
|
|
223
|
+
If you get connection errors:
|
|
224
|
+
|
|
225
|
+
```ruby
|
|
226
|
+
PG::ConnectionBad: could not connect to server
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Check:
|
|
230
|
+
1. PostgreSQL is running: `pg_isready`
|
|
231
|
+
2. Connection parameters are correct
|
|
232
|
+
3. User has proper permissions
|
|
233
|
+
4. `pg_hba.conf` allows the connection
|
|
234
|
+
|
|
235
|
+
### Missing Tables
|
|
236
|
+
|
|
237
|
+
If tables aren't found:
|
|
238
|
+
|
|
239
|
+
```ruby
|
|
240
|
+
generator.table_exists? # => false
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Check:
|
|
244
|
+
1. Table is in the `public` schema (or adjust schema in queries)
|
|
245
|
+
2. User has SELECT permission on the table
|
|
246
|
+
3. Table name is correct (case-sensitive in PostgreSQL when quoted)
|
|
247
|
+
|
|
248
|
+
### Gem Installation Issues
|
|
249
|
+
|
|
250
|
+
On macOS with Homebrew PostgreSQL:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
gem install pg -- --with-pg-config=/usr/local/bin/pg_config
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Or with a specific PostgreSQL version:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
gem install pg -- --with-pg-config=/usr/local/opt/postgresql@14/bin/pg_config
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Future Enhancements
|
|
263
|
+
|
|
264
|
+
Possible future improvements:
|
|
265
|
+
|
|
266
|
+
- Support for PostgreSQL-specific features (arrays, JSON columns)
|
|
267
|
+
- Custom type (enum, composite) detection
|
|
268
|
+
- Partitioned table support
|
|
269
|
+
- Foreign data wrapper detection
|
|
270
|
+
- Materialized view support
|
|
271
|
+
- PostgreSQL extension detection (PostGIS, etc.)
|
|
272
|
+
|
|
273
|
+
## References
|
|
274
|
+
|
|
275
|
+
- [PostgreSQL Documentation](https://www.postgresql.org/docs/)
|
|
276
|
+
- [pg gem documentation](https://github.com/ged/ruby-pg)
|
|
277
|
+
- [PostgreSQL INFORMATION_SCHEMA](https://www.postgresql.org/docs/current/information-schema.html)
|
|
278
|
+
- [PostgreSQL System Catalogs](https://www.postgresql.org/docs/current/catalogs.html)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# PostgreSQL Docker Setup for Database Model Generator
|
|
2
|
+
|
|
3
|
+
This directory contains Docker setup files for testing the PostgreSQL model generator.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Docker
|
|
8
|
+
- Docker Compose
|
|
9
|
+
|
|
10
|
+
## Quick Start
|
|
11
|
+
|
|
12
|
+
1. Start the PostgreSQL container:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
cd docker/postgresql
|
|
16
|
+
docker-compose up -d postgres-db
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
2. Wait for PostgreSQL to be ready (check with):
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
docker-compose logs -f postgres-db
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
3. Run the tests:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
docker-compose run --rm dmg_test
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Or run tests directly:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
docker-compose run --rm dmg_test bundle exec rspec spec/postgresql_model_generator_spec.rb
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Environment Variables
|
|
38
|
+
|
|
39
|
+
The following environment variables are available:
|
|
40
|
+
|
|
41
|
+
- `POSTGRES_HOST`: PostgreSQL host (default: postgres-db)
|
|
42
|
+
- `POSTGRES_PORT`: PostgreSQL port (default: 5432)
|
|
43
|
+
- `POSTGRES_USER`: PostgreSQL username (default: postgres)
|
|
44
|
+
- `POSTGRES_PASSWORD`: PostgreSQL password (optional, omit if using trust authentication)
|
|
45
|
+
- `POSTGRES_DATABASE`: PostgreSQL database name (default: test_db)
|
|
46
|
+
|
|
47
|
+
## Sample Database
|
|
48
|
+
|
|
49
|
+
The `init-db.sql` file creates a sample database schema with:
|
|
50
|
+
|
|
51
|
+
- **users** table - with status and role enum columns
|
|
52
|
+
- **posts** table - with foreign key to users, status and priority enums
|
|
53
|
+
- **comments** table - with polymorphic association (commentable)
|
|
54
|
+
- **tags** table - simple lookup table
|
|
55
|
+
- **post_tags** table - many-to-many join table
|
|
56
|
+
|
|
57
|
+
This schema is designed to test various features of the model generator including:
|
|
58
|
+
- Foreign key detection
|
|
59
|
+
- Enum column detection
|
|
60
|
+
- Polymorphic associations
|
|
61
|
+
- Primary keys
|
|
62
|
+
- Constraints
|
|
63
|
+
- Indexes
|
|
64
|
+
- Full-text search recommendations
|
|
65
|
+
|
|
66
|
+
## Manual Testing
|
|
67
|
+
|
|
68
|
+
To connect to PostgreSQL manually:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
docker-compose exec postgres-db psql -U postgres -d test_db
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Stopping and Cleaning Up
|
|
75
|
+
|
|
76
|
+
Stop the containers:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
docker-compose down
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Stop and remove volumes (warning: this deletes all data):
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
docker-compose down -v
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Troubleshooting
|
|
89
|
+
|
|
90
|
+
### PostgreSQL won't start
|
|
91
|
+
|
|
92
|
+
Check the logs:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
docker-compose logs postgres-db
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Connection refused
|
|
99
|
+
|
|
100
|
+
Make sure the healthcheck passes:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
docker-compose ps
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The postgres-db service should show as "healthy".
|
|
107
|
+
|
|
108
|
+
### Tests fail
|
|
109
|
+
|
|
110
|
+
Ensure the database is initialized properly:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
docker-compose exec postgres-db psql -U postgres -d test_db -c "\dt"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
You should see the tables listed.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
services:
|
|
2
|
+
# PostgreSQL database for testing
|
|
3
|
+
postgres-db:
|
|
4
|
+
image: postgres:16-alpine
|
|
5
|
+
container_name: dmg_postgres
|
|
6
|
+
environment:
|
|
7
|
+
- POSTGRES_USER=postgres
|
|
8
|
+
- POSTGRES_PASSWORD=postgres
|
|
9
|
+
- POSTGRES_DB=test_db
|
|
10
|
+
ports:
|
|
11
|
+
- "5432:5432"
|
|
12
|
+
volumes:
|
|
13
|
+
- postgres_data:/var/lib/postgresql/data
|
|
14
|
+
- ./init-db.sql:/docker-entrypoint-initdb.d/init-db.sql
|
|
15
|
+
healthcheck:
|
|
16
|
+
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
|
17
|
+
interval: 10s
|
|
18
|
+
timeout: 5s
|
|
19
|
+
retries: 5
|
|
20
|
+
start_period: 30s
|
|
21
|
+
networks:
|
|
22
|
+
- dmg_network
|
|
23
|
+
|
|
24
|
+
# Optional: Add a container to test the generator
|
|
25
|
+
dmg_test:
|
|
26
|
+
build:
|
|
27
|
+
context: ../../
|
|
28
|
+
dockerfile: docker/postgresql/test-Dockerfile
|
|
29
|
+
container_name: dmg_test_runner
|
|
30
|
+
depends_on:
|
|
31
|
+
postgres-db:
|
|
32
|
+
condition: service_healthy
|
|
33
|
+
volumes:
|
|
34
|
+
- ../../:/app
|
|
35
|
+
working_dir: /app
|
|
36
|
+
environment:
|
|
37
|
+
- POSTGRES_HOST=postgres-db
|
|
38
|
+
- POSTGRES_PORT=5432
|
|
39
|
+
- POSTGRES_USER=postgres
|
|
40
|
+
- POSTGRES_PASSWORD=postgres
|
|
41
|
+
- POSTGRES_DATABASE=test_db
|
|
42
|
+
networks:
|
|
43
|
+
- dmg_network
|
|
44
|
+
command: ["./docker/postgresql/test.sh"]
|
|
45
|
+
|
|
46
|
+
volumes:
|
|
47
|
+
postgres_data:
|
|
48
|
+
|
|
49
|
+
networks:
|
|
50
|
+
dmg_network:
|
|
51
|
+
driver: bridge
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
-- PostgreSQL test database initialization script
|
|
2
|
+
-- Create sample tables for testing the model generator
|
|
3
|
+
|
|
4
|
+
-- Create users table
|
|
5
|
+
CREATE TABLE users (
|
|
6
|
+
id SERIAL PRIMARY KEY,
|
|
7
|
+
username VARCHAR(50) NOT NULL UNIQUE,
|
|
8
|
+
email VARCHAR(100) NOT NULL UNIQUE,
|
|
9
|
+
first_name VARCHAR(50),
|
|
10
|
+
last_name VARCHAR(50),
|
|
11
|
+
status VARCHAR(20) DEFAULT 'active',
|
|
12
|
+
role VARCHAR(20) DEFAULT 'user',
|
|
13
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
14
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
15
|
+
CONSTRAINT check_status CHECK (status IN ('active', 'inactive', 'suspended', 'deleted')),
|
|
16
|
+
CONSTRAINT check_role CHECK (role IN ('user', 'admin', 'moderator'))
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
-- Create posts table
|
|
20
|
+
CREATE TABLE posts (
|
|
21
|
+
id SERIAL PRIMARY KEY,
|
|
22
|
+
user_id INTEGER NOT NULL,
|
|
23
|
+
title VARCHAR(200) NOT NULL,
|
|
24
|
+
content TEXT,
|
|
25
|
+
status VARCHAR(20) DEFAULT 'draft',
|
|
26
|
+
priority VARCHAR(10) DEFAULT 'medium',
|
|
27
|
+
published_at TIMESTAMP,
|
|
28
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
29
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
30
|
+
CONSTRAINT fk_posts_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
31
|
+
CONSTRAINT check_post_status CHECK (status IN ('draft', 'published', 'archived')),
|
|
32
|
+
CONSTRAINT check_priority CHECK (priority IN ('low', 'medium', 'high', 'urgent'))
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
-- Create comments table (with polymorphic association)
|
|
36
|
+
CREATE TABLE comments (
|
|
37
|
+
id SERIAL PRIMARY KEY,
|
|
38
|
+
user_id INTEGER NOT NULL,
|
|
39
|
+
commentable_id INTEGER NOT NULL,
|
|
40
|
+
commentable_type VARCHAR(50) NOT NULL,
|
|
41
|
+
content TEXT NOT NULL,
|
|
42
|
+
status VARCHAR(20) DEFAULT 'active',
|
|
43
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
44
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
45
|
+
CONSTRAINT fk_comments_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
46
|
+
CONSTRAINT check_comment_status CHECK (status IN ('active', 'hidden', 'deleted'))
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
-- Create tags table
|
|
50
|
+
CREATE TABLE tags (
|
|
51
|
+
id SERIAL PRIMARY KEY,
|
|
52
|
+
name VARCHAR(50) NOT NULL UNIQUE,
|
|
53
|
+
slug VARCHAR(60) NOT NULL UNIQUE,
|
|
54
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
-- Create post_tags (join table)
|
|
58
|
+
CREATE TABLE post_tags (
|
|
59
|
+
post_id INTEGER NOT NULL,
|
|
60
|
+
tag_id INTEGER NOT NULL,
|
|
61
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
62
|
+
PRIMARY KEY (post_id, tag_id),
|
|
63
|
+
CONSTRAINT fk_post_tags_post FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
|
|
64
|
+
CONSTRAINT fk_post_tags_tag FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
-- Create indexes for better query performance
|
|
68
|
+
CREATE INDEX idx_posts_user_id ON posts(user_id);
|
|
69
|
+
CREATE INDEX idx_posts_status ON posts(status);
|
|
70
|
+
CREATE INDEX idx_posts_created_at ON posts(created_at);
|
|
71
|
+
CREATE INDEX idx_comments_user_id ON comments(user_id);
|
|
72
|
+
CREATE INDEX idx_comments_commentable ON comments(commentable_id, commentable_type);
|
|
73
|
+
|
|
74
|
+
-- Create a full-text search index
|
|
75
|
+
CREATE INDEX idx_posts_title_content_text ON posts USING GIN (to_tsvector('english', title || ' ' || COALESCE(content, '')));
|
|
76
|
+
|
|
77
|
+
-- Insert sample data
|
|
78
|
+
INSERT INTO users (username, email, first_name, last_name, status, role) VALUES
|
|
79
|
+
('john_doe', 'john@example.com', 'John', 'Doe', 'active', 'admin'),
|
|
80
|
+
('jane_smith', 'jane@example.com', 'Jane', 'Smith', 'active', 'user'),
|
|
81
|
+
('bob_jones', 'bob@example.com', 'Bob', 'Jones', 'inactive', 'moderator');
|
|
82
|
+
|
|
83
|
+
INSERT INTO posts (user_id, title, content, status, priority) VALUES
|
|
84
|
+
(1, 'First Post', 'This is the content of the first post', 'published', 'high'),
|
|
85
|
+
(1, 'Second Post', 'This is the content of the second post', 'draft', 'medium'),
|
|
86
|
+
(2, 'Jane''s Post', 'Jane writes about something interesting', 'published', 'low');
|
|
87
|
+
|
|
88
|
+
INSERT INTO tags (name, slug) VALUES
|
|
89
|
+
('Technology', 'technology'),
|
|
90
|
+
('Ruby', 'ruby'),
|
|
91
|
+
('PostgreSQL', 'postgresql');
|
|
92
|
+
|
|
93
|
+
INSERT INTO post_tags (post_id, tag_id) VALUES
|
|
94
|
+
(1, 1),
|
|
95
|
+
(1, 2),
|
|
96
|
+
(3, 1),
|
|
97
|
+
(3, 3);
|
|
98
|
+
|
|
99
|
+
INSERT INTO comments (user_id, commentable_id, commentable_type, content) VALUES
|
|
100
|
+
(2, 1, 'Post', 'Great post!'),
|
|
101
|
+
(3, 1, 'Post', 'Thanks for sharing');
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
FROM ruby:3.2-alpine
|
|
2
|
+
|
|
3
|
+
# Install build dependencies
|
|
4
|
+
RUN apk add --no-cache \
|
|
5
|
+
build-base \
|
|
6
|
+
postgresql-dev \
|
|
7
|
+
postgresql-client \
|
|
8
|
+
git
|
|
9
|
+
|
|
10
|
+
# Set working directory
|
|
11
|
+
WORKDIR /app
|
|
12
|
+
|
|
13
|
+
# Copy application files
|
|
14
|
+
COPY . .
|
|
15
|
+
|
|
16
|
+
# Install dependencies
|
|
17
|
+
RUN bundle install
|
|
18
|
+
|
|
19
|
+
# Run tests
|
|
20
|
+
CMD ["bundle", "exec", "rspec", "spec/postgresql_model_generator_spec.rb"]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
echo "Waiting for PostgreSQL to be ready..."
|
|
5
|
+
until pg_isready -h $POSTGRES_HOST -p $POSTGRES_PORT -U $POSTGRES_USER; do
|
|
6
|
+
echo "PostgreSQL is unavailable - sleeping"
|
|
7
|
+
sleep 2
|
|
8
|
+
done
|
|
9
|
+
|
|
10
|
+
echo "PostgreSQL is up - running tests"
|
|
11
|
+
|
|
12
|
+
# Install dependencies if needed
|
|
13
|
+
bundle check || bundle install
|
|
14
|
+
|
|
15
|
+
# Run the tests
|
|
16
|
+
bundle exec rspec spec/postgresql_model_generator_spec.rb -fd
|
|
17
|
+
|
|
18
|
+
echo "Tests completed!"
|
|
@@ -10,10 +10,16 @@ rescue LoadError
|
|
|
10
10
|
# TinyTDS not available - SQL Server support will be disabled
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
+
begin
|
|
14
|
+
require 'pg'
|
|
15
|
+
rescue LoadError
|
|
16
|
+
# pg not available - PostgreSQL support will be disabled
|
|
17
|
+
end
|
|
18
|
+
|
|
13
19
|
module DatabaseModel
|
|
14
20
|
module Generator
|
|
15
21
|
# The version of the database-model-generator library
|
|
16
|
-
VERSION = '0.
|
|
22
|
+
VERSION = '0.7.0'
|
|
17
23
|
|
|
18
24
|
# Factory method to create the appropriate generator based on connection type
|
|
19
25
|
def self.new(connection, options = {})
|
|
@@ -26,6 +32,9 @@ module DatabaseModel
|
|
|
26
32
|
when :sqlserver
|
|
27
33
|
require_relative 'sqlserver/model/generator'
|
|
28
34
|
SqlServer::Model::Generator.new(connection)
|
|
35
|
+
when :postgresql
|
|
36
|
+
require_relative 'postgresql/model/generator'
|
|
37
|
+
Postgresql::Model::Generator.new(connection)
|
|
29
38
|
else
|
|
30
39
|
raise ArgumentError, "Unsupported database type: #{database_type}"
|
|
31
40
|
end
|
|
@@ -41,6 +50,8 @@ module DatabaseModel
|
|
|
41
50
|
:oracle
|
|
42
51
|
when 'TinyTds::Client'
|
|
43
52
|
:sqlserver
|
|
53
|
+
when 'PG::Connection'
|
|
54
|
+
:postgresql
|
|
44
55
|
else
|
|
45
56
|
raise ArgumentError, "Cannot detect database type from connection: #{connection.class}"
|
|
46
57
|
end
|