sqlobjects 0.1.0__py3-none-any.whl

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.
sqlobjects/__init__.py ADDED
@@ -0,0 +1,38 @@
1
+ """SQLObjects - Django-style async ORM library built on SQLAlchemy Core
2
+
3
+ A modern, async-first ORM library that provides Django-style APIs while leveraging
4
+ the power and performance of SQLAlchemy Core.
5
+
6
+ Core Features:
7
+ - Django-style chainable queries with Q objects
8
+ - Async-first design with SQLAlchemy Core
9
+ - Multi-database support with connection management
10
+ - Comprehensive validation system
11
+ - Signal system for model lifecycle events
12
+ - Type-safe field definitions with shortcuts
13
+
14
+ Examples:
15
+ >>> from sqlobjects import ObjectModel
16
+ >>> from sqlobjects.database import init_db, create_tables
17
+ >>> from sqlobjects.fields import str_column, int_column
18
+ >>> # Initialize database
19
+ >>> db = await init_db("sqlite+aiosqlite:///test.db")
20
+ >>> # Define model
21
+ >>> class User(ObjectModel):
22
+ ... name: str = str_column(length=50)
23
+ ... age: int = int_column()
24
+ >>> # Create tables
25
+ >>> await db.create_tables(ObjectModel)
26
+ >>> # Use the model
27
+ >>> user = await User.objects.create(name="John", age=25)
28
+ """
29
+
30
+ from .model import ObjectModel
31
+ from .queries import Q
32
+
33
+
34
+ __version__ = "2.0.0"
35
+ __all__ = [
36
+ "ObjectModel",
37
+ "Q",
38
+ ]
sqlobjects/config.py ADDED
@@ -0,0 +1,519 @@
1
+ from collections.abc import Callable
2
+ from typing import Any
3
+
4
+
5
+ __all__ = [
6
+ "database_specific",
7
+ "mysql_config",
8
+ "postgresql_config",
9
+ "sqlite_config",
10
+ "multi_db_config",
11
+ "high_performance_mysql",
12
+ "compressed_mysql",
13
+ "read_only_mysql",
14
+ "memory_mysql",
15
+ "high_performance_postgresql",
16
+ "analytics_postgresql",
17
+ "optimized_sqlite",
18
+ ]
19
+
20
+
21
+ _DEFAULT_MYSQL_ENGINE = "InnoDB"
22
+ _DEFAULT_CHARSET = "utf8mb4"
23
+
24
+
25
+ def database_specific(**configs: dict[str, Any]) -> Callable[[str], dict[str, Any]]:
26
+ """Create database-specific configuration factory function.
27
+
28
+ This function creates a configuration factory that can return different
29
+ configurations based on the database dialect being used. It's useful for
30
+ creating models that need different settings for different databases.
31
+
32
+ Args:
33
+ **configs: Database-specific configurations where keys are database
34
+ dialect names ('postgresql', 'mysql', 'sqlite') and values
35
+ are configuration dictionaries
36
+
37
+ Returns:
38
+ Function that takes a dialect name and returns the appropriate configuration
39
+
40
+ Examples:
41
+ >>> db_config = database_specific(
42
+ ... postgresql={"tablespace": "fast_storage"},
43
+ ... mysql={"engine": "InnoDB", "charset": "utf8mb4"},
44
+ ... sqlite={"without_rowid": True},
45
+ ... )
46
+ >>> mysql_opts = db_config("mysql")
47
+ >>> postgresql_opts = db_config("postgresql")
48
+ """
49
+
50
+ def _get_config_for_dialect(dialect_name: str) -> dict[str, Any]:
51
+ """Get configuration for the specified database dialect.
52
+
53
+ Args:
54
+ dialect_name: Name of the database dialect (e.g., 'mysql', 'postgresql', 'sqlite')
55
+
56
+ Returns:
57
+ Configuration dictionary for the specified dialect, or empty dict if not found
58
+ """
59
+ return configs.get(dialect_name, {})
60
+
61
+ # For now, return a simple function
62
+ # In a real implementation, this would detect the current database dialect
63
+ return _get_config_for_dialect
64
+
65
+
66
+ def mysql_config(
67
+ engine: str = _DEFAULT_MYSQL_ENGINE,
68
+ charset: str = _DEFAULT_CHARSET,
69
+ collate: str | None = None,
70
+ row_format: str | None = None,
71
+ key_block_size: int | None = None,
72
+ auto_increment: int | None = None,
73
+ avg_row_length: int | None = None, # Expected average row length for MyISAM optimization
74
+ checksum: bool | None = None, # Maintain live checksum for MyISAM tables (slower writes, faster integrity checks)
75
+ comment: str | None = None,
76
+ connection: str | None = None,
77
+ data_directory: str | None = None,
78
+ delay_key_write: bool | None = None, # Delay key writes for MyISAM (faster bulk inserts, risk of corruption)
79
+ index_directory: str | None = None,
80
+ insert_method: str | None = None,
81
+ max_rows: int | None = None,
82
+ min_rows: int | None = None,
83
+ pack_keys: bool | str | None = None, # Pack string keys (True/False/'DEFAULT') - saves space but slower access
84
+ password: str | None = None,
85
+ stats_auto_recalc: bool | None = None, # Auto recalculate InnoDB statistics when 10% of table changes
86
+ stats_persistent: bool | None = None, # Store InnoDB statistics persistently across server restarts
87
+ stats_sample_pages: int | None = None, # Number of index pages to sample for statistics (1-65535)
88
+ tablespace: str | None = None,
89
+ union: str | None = None,
90
+ **kwargs: Any,
91
+ ) -> dict[str, dict[str, Any]]:
92
+ """Create MySQL-specific configuration with comprehensive options.
93
+
94
+ Args:
95
+ engine: Storage engine (InnoDB, MyISAM, Memory, Archive, CSV, etc.)
96
+ charset: Character set (utf8, utf8mb4, latin1, ascii, etc.)
97
+ collate: Collation rule (utf8mb4_unicode_ci, utf8mb4_general_ci, etc.)
98
+ row_format: Row format (DYNAMIC, FIXED, COMPRESSED, REDUNDANT, COMPACT)
99
+ key_block_size: Key block size for compressed tables (1, 2, 4, 8, 16)
100
+ auto_increment: Initial AUTO_INCREMENT value
101
+ avg_row_length: Average row length for MyISAM tables
102
+ checksum: Whether to maintain live checksum for MyISAM tables
103
+ comment: Table comment (up to 2048 characters)
104
+ connection: Connection string for federated tables
105
+ data_directory: Data directory path for MyISAM tables
106
+ delay_key_write: Delay key writes for MyISAM tables
107
+ index_directory: Index directory path for MyISAM tables
108
+ insert_method: Insert method for MERGE tables (NO, FIRST, LAST)
109
+ max_rows: Maximum number of rows
110
+ min_rows: Minimum number of rows
111
+ pack_keys: Pack keys option (True, False, 'DEFAULT')
112
+ password: Password for table encryption
113
+ stats_auto_recalc: Auto recalculate statistics for InnoDB
114
+ stats_persistent: Persistent statistics for InnoDB
115
+ stats_sample_pages: Sample pages for InnoDB statistics
116
+ tablespace: Tablespace name for InnoDB
117
+ union: Union tables for MERGE engine
118
+ **kwargs: Additional MySQL table options
119
+
120
+ Returns:
121
+ Dictionary with MySQL configuration
122
+
123
+ Examples:
124
+ >>> mysql_config(engine="InnoDB", charset="utf8mb4")
125
+ >>> mysql_config(engine="MyISAM", row_format="COMPRESSED", checksum=True)
126
+ >>> mysql_config(engine="InnoDB", tablespace="innodb_file_per_table")
127
+ >>> mysql_config(engine="Memory", max_rows=1000000)
128
+ """
129
+ config = {"engine": engine, "charset": charset}
130
+
131
+ # Add optional parameters if provided
132
+ optional_params = {
133
+ "collate": collate,
134
+ "row_format": row_format,
135
+ "key_block_size": key_block_size,
136
+ "auto_increment": auto_increment,
137
+ "avg_row_length": avg_row_length,
138
+ "checksum": checksum,
139
+ "comment": comment,
140
+ "connection": connection,
141
+ "data_directory": data_directory,
142
+ "delay_key_write": delay_key_write,
143
+ "index_directory": index_directory,
144
+ "insert_method": insert_method,
145
+ "max_rows": max_rows,
146
+ "min_rows": min_rows,
147
+ "pack_keys": pack_keys,
148
+ "password": password,
149
+ "stats_auto_recalc": stats_auto_recalc,
150
+ "stats_persistent": stats_persistent,
151
+ "stats_sample_pages": stats_sample_pages,
152
+ "tablespace": tablespace,
153
+ "union": union,
154
+ }
155
+
156
+ for key, value in optional_params.items():
157
+ if value is not None:
158
+ config[key] = value
159
+
160
+ config.update(kwargs)
161
+ return {"mysql": config}
162
+
163
+
164
+ def postgresql_config(
165
+ tablespace: str | None = None,
166
+ with_oids: bool | None = None,
167
+ fillfactor: int | None = None, # Page fill factor (10-100) - lower values leave space for updates
168
+ toast_tuple_target: int
169
+ | None = None, # TOAST compression threshold (128-8160 bytes) - when to compress large values
170
+ parallel_workers: int | None = None, # Max parallel workers for queries on this table (0-1024)
171
+ autovacuum_enabled: bool | None = None,
172
+ autovacuum_vacuum_threshold: int | None = None,
173
+ autovacuum_vacuum_scale_factor: float
174
+ | None = None, # Fraction of table size to add to vacuum threshold (0.0-100.0)
175
+ autovacuum_analyze_threshold: int | None = None,
176
+ autovacuum_analyze_scale_factor: float
177
+ | None = None, # Fraction of table size to add to analyze threshold (0.0-100.0)
178
+ autovacuum_vacuum_cost_delay: int | None = None,
179
+ autovacuum_vacuum_cost_limit: int | None = None,
180
+ autovacuum_freeze_min_age: int
181
+ | None = None, # Minimum age for freezing tuples (0-1000000000) - prevents premature freezing
182
+ autovacuum_freeze_max_age: int
183
+ | None = None, # Maximum age before forced vacuum (0-2000000000) - prevents wraparound
184
+ autovacuum_freeze_table_age: int | None = None,
185
+ log_autovacuum_min_duration: int | None = None,
186
+ user_catalog_table: bool | None = None, # Whether table is a user catalog table (affects system catalog behavior)
187
+ **kwargs: Any,
188
+ ) -> dict[str, dict[str, Any]]:
189
+ """Create PostgreSQL-specific configuration with comprehensive options.
190
+
191
+ Args:
192
+ tablespace: Tablespace name for the table
193
+ with_oids: Whether to create table with OIDs (deprecated in PostgreSQL 12+)
194
+ fillfactor: Fill factor percentage (10-100) for table pages
195
+ toast_tuple_target: Target for TOAST compression (128-8160 bytes)
196
+ parallel_workers: Number of parallel workers for queries
197
+ autovacuum_enabled: Enable/disable autovacuum for this table
198
+ autovacuum_vacuum_threshold: Minimum number of updated/deleted tuples before vacuum
199
+ autovacuum_vacuum_scale_factor: Fraction of table size to add to vacuum threshold
200
+ autovacuum_analyze_threshold: Minimum number of inserted/updated/deleted tuples before analyze
201
+ autovacuum_analyze_scale_factor: Fraction of table size to add to analyze threshold
202
+ autovacuum_vacuum_cost_delay: Cost delay for autovacuum (milliseconds)
203
+ autovacuum_vacuum_cost_limit: Cost limit for autovacuum
204
+ autovacuum_freeze_min_age: Minimum age for freezing tuples
205
+ autovacuum_freeze_max_age: Maximum age before forced vacuum
206
+ autovacuum_freeze_table_age: Age at which to scan whole table for freezing
207
+ log_autovacuum_min_duration: Minimum duration to log autovacuum actions
208
+ user_catalog_table: Whether table is a user catalog table
209
+ **kwargs: Additional PostgreSQL table options
210
+
211
+ Returns:
212
+ Dictionary with PostgreSQL configuration
213
+
214
+ Examples:
215
+ >>> postgresql_config(tablespace="fast_storage")
216
+ >>> postgresql_config(fillfactor=80, autovacuum_enabled=True)
217
+ >>> postgresql_config(parallel_workers=4, toast_tuple_target=2048)
218
+ >>> postgresql_config(autovacuum_vacuum_scale_factor=0.1)
219
+ """
220
+ config = {}
221
+
222
+ # Add optional parameters if provided
223
+ optional_params = {
224
+ "tablespace": tablespace,
225
+ "with_oids": with_oids,
226
+ "fillfactor": fillfactor,
227
+ "toast_tuple_target": toast_tuple_target,
228
+ "parallel_workers": parallel_workers,
229
+ "autovacuum_enabled": autovacuum_enabled,
230
+ "autovacuum_vacuum_threshold": autovacuum_vacuum_threshold,
231
+ "autovacuum_vacuum_scale_factor": autovacuum_vacuum_scale_factor,
232
+ "autovacuum_analyze_threshold": autovacuum_analyze_threshold,
233
+ "autovacuum_analyze_scale_factor": autovacuum_analyze_scale_factor,
234
+ "autovacuum_vacuum_cost_delay": autovacuum_vacuum_cost_delay,
235
+ "autovacuum_vacuum_cost_limit": autovacuum_vacuum_cost_limit,
236
+ "autovacuum_freeze_min_age": autovacuum_freeze_min_age,
237
+ "autovacuum_freeze_max_age": autovacuum_freeze_max_age,
238
+ "autovacuum_freeze_table_age": autovacuum_freeze_table_age,
239
+ "log_autovacuum_min_duration": log_autovacuum_min_duration,
240
+ "user_catalog_table": user_catalog_table,
241
+ }
242
+
243
+ for key, value in optional_params.items():
244
+ if value is not None:
245
+ config[key] = value
246
+
247
+ config.update(kwargs)
248
+ return {"postgresql": config}
249
+
250
+
251
+ def sqlite_config(
252
+ without_rowid: bool | None = None,
253
+ strict: bool | None = None,
254
+ **kwargs: Any,
255
+ ) -> dict[str, dict[str, Any]]:
256
+ """Create SQLite-specific configuration with comprehensive options.
257
+
258
+ Args:
259
+ without_rowid: Create table WITHOUT ROWID (more efficient for certain use cases)
260
+ strict: Enable strict type checking (SQLite 3.37.0+)
261
+ **kwargs: Additional SQLite table options
262
+
263
+ Returns:
264
+ Dictionary with SQLite configuration
265
+
266
+ Examples:
267
+ >>> sqlite_config(without_rowid=True)
268
+ >>> sqlite_config(strict=True)
269
+ >>> sqlite_config(without_rowid=True, strict=True)
270
+
271
+ Notes:
272
+ - WITHOUT ROWID tables are more efficient when:
273
+ * The table has a primary key
274
+ * The primary key is frequently used for lookups
275
+ * The table is read-only or read-mostly
276
+ - STRICT mode enforces type affinity (requires SQLite 3.37.0+)
277
+ """
278
+ config = {}
279
+
280
+ # Add optional parameters if provided
281
+ if without_rowid is not None:
282
+ config["without_rowid"] = without_rowid
283
+ if strict is not None:
284
+ config["strict"] = strict
285
+
286
+ config.update(kwargs)
287
+ return {"sqlite": config}
288
+
289
+
290
+ def multi_db_config(
291
+ mysql: dict[str, Any] | None = None,
292
+ postgresql: dict[str, Any] | None = None,
293
+ sqlite: dict[str, Any] | None = None,
294
+ generic: dict[str, Any] | None = None,
295
+ ) -> dict[str, dict[str, Any]]:
296
+ """Create multi-database configuration.
297
+
298
+ Args:
299
+ mysql: MySQL-specific options
300
+ postgresql: PostgreSQL-specific options
301
+ sqlite: SQLite-specific options
302
+ generic: Generic options applied to all databases
303
+
304
+ Returns:
305
+ Dictionary with multi-database configuration
306
+
307
+ Examples:
308
+ >>> multi_db_config(
309
+ ... mysql={"engine": "InnoDB", "charset": "utf8mb4"},
310
+ ... postgresql={"tablespace": "fast_storage"},
311
+ ... generic={"comment": "User data table"},
312
+ ... )
313
+ """
314
+ config = {}
315
+ if mysql:
316
+ config["mysql"] = mysql
317
+ if postgresql:
318
+ config["postgresql"] = postgresql
319
+ if sqlite:
320
+ config["sqlite"] = sqlite
321
+ if generic:
322
+ config["generic"] = generic
323
+ return config
324
+
325
+
326
+ # Specialized configuration functions for common use cases
327
+
328
+
329
+ def high_performance_mysql(
330
+ charset: str = _DEFAULT_CHARSET,
331
+ row_format: str = "DYNAMIC",
332
+ stats_persistent: bool = True,
333
+ stats_auto_recalc: bool = True,
334
+ **kwargs: Any,
335
+ ) -> dict[str, dict[str, Any]]:
336
+ """MySQL configuration optimized for high performance.
337
+
338
+ Args:
339
+ charset: Character set (default: utf8mb4)
340
+ row_format: Row format (default: DYNAMIC for better compression)
341
+ stats_persistent: Enable persistent statistics
342
+ stats_auto_recalc: Enable automatic statistics recalculation
343
+ **kwargs: Additional MySQL options
344
+
345
+ Returns:
346
+ Dictionary with high-performance MySQL configuration
347
+
348
+ Examples:
349
+ >>> high_performance_mysql()
350
+ >>> high_performance_mysql(key_block_size=8)
351
+ """
352
+ return mysql_config(
353
+ engine="InnoDB",
354
+ charset=charset,
355
+ row_format=row_format,
356
+ stats_persistent=stats_persistent,
357
+ stats_auto_recalc=stats_auto_recalc,
358
+ **kwargs,
359
+ )
360
+
361
+
362
+ def compressed_mysql(
363
+ charset: str = _DEFAULT_CHARSET,
364
+ row_format: str = "COMPRESSED",
365
+ key_block_size: int = 8,
366
+ **kwargs: Any,
367
+ ) -> dict[str, dict[str, Any]]:
368
+ """MySQL configuration for compressed storage.
369
+
370
+ Args:
371
+ charset: Character set (default: utf8mb4)
372
+ row_format: Row format (default: COMPRESSED)
373
+ key_block_size: Key block size for compression (default: 8)
374
+ **kwargs: Additional MySQL options
375
+
376
+ Returns:
377
+ Dictionary with compressed MySQL configuration
378
+
379
+ Examples:
380
+ >>> compressed_mysql()
381
+ >>> compressed_mysql(key_block_size=4)
382
+ """
383
+ return mysql_config(
384
+ engine=_DEFAULT_MYSQL_ENGINE, charset=charset, row_format=row_format, key_block_size=key_block_size, **kwargs
385
+ )
386
+
387
+
388
+ def read_only_mysql(
389
+ charset: str = _DEFAULT_CHARSET,
390
+ **kwargs: Any,
391
+ ) -> dict[str, dict[str, Any]]:
392
+ """MySQL configuration optimized for read-only tables.
393
+
394
+ Args:
395
+ charset: Character set (default: utf8mb4)
396
+ **kwargs: Additional MySQL options
397
+
398
+ Returns:
399
+ Dictionary with read-only optimized MySQL configuration
400
+
401
+ Examples:
402
+ >>> read_only_mysql()
403
+ >>> read_only_mysql(pack_keys=True)
404
+ """
405
+ return mysql_config(engine="MyISAM", charset=charset, pack_keys=True, **kwargs)
406
+
407
+
408
+ def memory_mysql(
409
+ charset: str = _DEFAULT_CHARSET,
410
+ max_rows: int = 1000000,
411
+ **kwargs: Any,
412
+ ) -> dict[str, dict[str, Any]]:
413
+ """MySQL configuration for in-memory tables.
414
+
415
+ Args:
416
+ charset: Character set (default: utf8mb4)
417
+ max_rows: Maximum number of rows (default: 1000000)
418
+ **kwargs: Additional MySQL options
419
+
420
+ Returns:
421
+ Dictionary with memory-optimized MySQL configuration
422
+
423
+ Examples:
424
+ >>> memory_mysql()
425
+ >>> memory_mysql(max_rows=500000)
426
+ """
427
+ return mysql_config(engine="Memory", charset=charset, max_rows=max_rows, **kwargs)
428
+
429
+
430
+ def high_performance_postgresql(
431
+ fillfactor: int = 90,
432
+ parallel_workers: int = 4,
433
+ autovacuum_enabled: bool = True,
434
+ autovacuum_vacuum_scale_factor: float = 0.1,
435
+ autovacuum_analyze_scale_factor: float = 0.05,
436
+ **kwargs: Any,
437
+ ) -> dict[str, dict[str, Any]]:
438
+ """PostgreSQL configuration optimized for high performance.
439
+
440
+ Args:
441
+ fillfactor: Fill factor for better update performance (default: 90)
442
+ parallel_workers: Number of parallel workers (default: 4)
443
+ autovacuum_enabled: Enable autovacuum (default: True)
444
+ autovacuum_vacuum_scale_factor: Vacuum scale factor (default: 0.1)
445
+ autovacuum_analyze_scale_factor: Analyze scale factor (default: 0.05)
446
+ **kwargs: Additional PostgreSQL options
447
+
448
+ Returns:
449
+ Dictionary with high-performance PostgreSQL configuration
450
+
451
+ Examples:
452
+ >>> high_performance_postgresql()
453
+ >>> high_performance_postgresql(parallel_workers=8)
454
+ """
455
+ return postgresql_config(
456
+ fillfactor=fillfactor,
457
+ parallel_workers=parallel_workers,
458
+ autovacuum_enabled=autovacuum_enabled,
459
+ autovacuum_vacuum_scale_factor=autovacuum_vacuum_scale_factor,
460
+ autovacuum_analyze_scale_factor=autovacuum_analyze_scale_factor,
461
+ **kwargs,
462
+ )
463
+
464
+
465
+ def analytics_postgresql(
466
+ fillfactor: int = 100,
467
+ parallel_workers: int = 8,
468
+ autovacuum_vacuum_scale_factor: float = 0.02,
469
+ autovacuum_analyze_scale_factor: float = 0.01,
470
+ toast_tuple_target: int = 2048,
471
+ **kwargs: Any,
472
+ ) -> dict[str, dict[str, Any]]:
473
+ """PostgreSQL configuration optimized for analytics workloads.
474
+
475
+ Args:
476
+ fillfactor: Fill factor for read-heavy workloads (default: 100)
477
+ parallel_workers: Number of parallel workers (default: 8)
478
+ autovacuum_vacuum_scale_factor: Lower vacuum frequency (default: 0.02)
479
+ autovacuum_analyze_scale_factor: More frequent analyze (default: 0.01)
480
+ toast_tuple_target: TOAST compression target (default: 2048)
481
+ **kwargs: Additional PostgreSQL options
482
+
483
+ Returns:
484
+ Dictionary with analytics-optimized PostgreSQL configuration
485
+
486
+ Examples:
487
+ >>> analytics_postgresql()
488
+ >>> analytics_postgresql(parallel_workers=16)
489
+ """
490
+ return postgresql_config(
491
+ fillfactor=fillfactor,
492
+ parallel_workers=parallel_workers,
493
+ autovacuum_vacuum_scale_factor=autovacuum_vacuum_scale_factor,
494
+ autovacuum_analyze_scale_factor=autovacuum_analyze_scale_factor,
495
+ toast_tuple_target=toast_tuple_target,
496
+ **kwargs,
497
+ )
498
+
499
+
500
+ def optimized_sqlite(
501
+ without_rowid: bool = True,
502
+ strict: bool = True,
503
+ **kwargs: Any,
504
+ ) -> dict[str, dict[str, Any]]:
505
+ """SQLite configuration with modern optimizations.
506
+
507
+ Args:
508
+ without_rowid: Use WITHOUT ROWID for better performance (default: True)
509
+ strict: Enable strict type checking (default: True)
510
+ **kwargs: Additional SQLite options
511
+
512
+ Returns:
513
+ Dictionary with optimized SQLite configuration
514
+
515
+ Examples:
516
+ >>> optimized_sqlite()
517
+ >>> optimized_sqlite(without_rowid=False)
518
+ """
519
+ return sqlite_config(without_rowid=without_rowid, strict=strict, **kwargs)