Todas las referencias de archivo:línea a continuación están vigentes a fecha de 2026-10-25.
Un driver son dos clases pequeñas y una llamada de registro:
MADDatabase (MADAPI/Source/MADDatabase.h — la cabecera
lo dice claramente: «This class is built for subclassing») que sabe cómo
explorar el esquema de una fuente y ejecutar una consulta contra ella;MADDatabaseConnection que implementa
el protocolo MADDatabaseDriver
(MADAPI/Source/MADDatabaseDriverRegistry.h) y se registra en
+load.Todo lo demás —el grafo del diseñador de consultas, la generación de SQL, la caché de
filas, la canalización de renderizado, los siete formatos de exportación, la CLI y la imagen de
Docker— es maquinaria compartida a la que el driver se conecta. El contrato que cumple un driver es
«devolver un esquema y filas en forma de tabla»
(docs/project/MAD_STRATEGY.md §3.7), y por eso un driver puede estar frente a
cualquier cosa que se pueda hacer parecer una tabla: una base de datos SQL, un servicio
web, un archivo.
Los tres ejemplos de servidor de los que tomar ejemplo son MADPostgres (6 archivos,
MADPostgres/Source/, subclase MADDatabasePostgres sobre libpq),
MADMySQL (18 archivos, MADMySQL/Source/, subclase MADDatabaseMySQL) y
MADTdsClient (SQL Server sobre FreeTDS). Sqlite3 es el cuarto driver distribuido
(MADSqlite3), y el caso respaldado por archivo
(+newDatabaseConnectionWithURL:) se muestra en su clase de conexión.
Elija el siguiente valor libre de MADDatabaseType (MADAPI/Source/MADDatabaseType.h;
los tipos distribuidos son 1–12) o amplíe la enumeración. Es lo que el documento
almacena, así que es una constante de formato: una vez que existen documentos con su tipo,
el número es permanente.
MADDatabaseLas costuras abstractas viven en MADAPI/Source/MADDatabaseProtocol.h
(MADDatabaseDataSource). La superficie obligatoria:
- (void)getTables:(void (^)(BOOL success, NSError *error,
NSArray <MADDatabaseTable *> *tables))completion;
- (NSArray <MADDatabaseTable *> *)getTablesFromConnection:(id)dbconn
error:(NSError **)error;
- (NSArray <MADDatabaseField *> *)fieldsForTable:(NSString *)tableName
connection:(id)dbconn
error:(NSError **)error;
- (id)connectWithConnection:(MADDatabaseConnection *)connection
credentals:(MADCredential *)credentals
connectionError:(NSError **)error;
// The execution seam: the driver fills the table it is handed. The
// destination is any <MADDataTableWriting>, which is what keeps the
// promise to "anything that returns a table" (MADDataTable.h).
- (BOOL)executeSQL:(NSString *)sql
fieldKeys:(NSArray *)fieldKeys
onConnection:(id)dbconn
intoResultTable:(id <MADDataTableWriting>)dataTable
withError:(NSError **)error;
- (BOOL)executeQuery:(MADQuery *)query withError:(NSError **)error;
- (BOOL)executeQuery:(MADQuery *)query
withDataTable:(id <MADDataTableWriting>)dataTable
andError:(NSError **)error;
// Optional: only file-backed drivers have nothing to dial.
- (void)closeConnection:(id)process;
La exploración del esquema (getTables…, fieldsForTable:…) es lo que llama el flujo de
creación de documentos de la aplicación cuando importa el esquema de una base de datos a un nuevo
.mad; la costura de ejecución es lo que llaman mad refresh y el visor.
MADOperationRefresh.m:134 muestra la llamada exacta que hace la cadena de actualización.
@interface MyDriverConnection : MADDatabaseConnection <MADDatabaseDriver>
@end
@implementation MyDriverConnection
+ (void)load {
[MADDatabaseDriverRegistry registerDriverClass: self];
}
+ (MADDatabaseType)databaseType { return MADDatabaseTypeMyDriver; }
+ (NSString *)Identifier { return @"MyDriver"; } // stored in documents
+ (instancetype)newDatabaseConnection:(NSObject <MADConnectionsPropertiesProtocol> *)credentials { … }
+ (Class)databaseClass { return [MyDatabase class]; }
+ (instancetype)newDatabaseConnectionWithHost:(NSString *)host
database:(NSString *)database
port:(NSInteger)port { … }
@end
+Identifier es lo que contiene RptqDatabaseCredentals.driverID, así que es una
constante del formato del documento: elija una grafía estable («MySQL», «TDS»,
«PostgreSQL», «Sqlite 3» son las distribuidas). Una segunda clase que reclame un tipo ya tomado es un error de
programación; el registro lo comprueba con una aserción.
El registro es por proceso y por enlace: un proceso que no enlaza su driver no puede construir sus bases de datos (el constructor falla con una aserción y un mensaje claro en lugar de fallar más tarde con nil).
FRAMEWORKS de CLI/build.sh), o use -ObjC al enlazar un
archivo estático; de lo contrario, las categorías y los métodos +load se descartan
silenciosamente.Linux: añada su archivo con whole-archive al objetivo mad en
Linux/CMakeLists.txt, tal como lo están madsqlite3, madmysql, madtdsclient
y madpostgres:
target_link_libraries(mad PUBLIC
"$<LINK_LIBRARY:WHOLE_ARCHIVE,my_driver>" …)
No es un detalle menor: el trabajo de un driver se alcanza a través de categorías
(envíos de mensajes, sin símbolos), y un enlace estático que no sea whole-archive los descarta: el
fallo es una caída en el primer envío de mensaje, no una advertencia del enlazador
(Linux/README.md, «The compat shims» / «The drivers are registered»).
Una lista de fuentes por driver en cmake/MyDriverSources.cmake sigue la
forma de MADFoundationSources.cmake, con cada archivo excluido nombrado y
el motivo.
Dos suites, en el objetivo de pruebas del framework:
MADMySQL/MADMySQLTests/MADMySQLLiveTests.m es
la plantilla: las credenciales toman como valor predeterminado un servidor local documentado y
todas se pueden sobrescribir con una variable de entorno
(MAD_MYSQL_TEST_HOST, MAD_MYSQL_TEST_PORT, MAD_MYSQL_TEST_USER,
MAD_MYSQL_TEST_PASSWORD, MAD_MYSQL_TEST_SCHEMA), y cada prueba en vivo
empieza con XCTSkipIf(![self serverIsAvailable], …). Una máquina
sin su fuente sigue obteniendo una suite verde. Observe el comentario del archivo MySQL: use
127.0.0.1, no localhost, dentro del Simulador de iOS.MADDatabaseDriverRegistryTests muestra la forma:
registre un driver de prueba, compruebe la búsqueda, compruebe el lanzamiento por tipo
duplicado. Su driver obtiene una de estas gratis por construcción.Linux/Tests/ si el driver pertenece a la
compilación sin interfaz gráfica (consulte cmake/ y «Adding the next
framework» en Linux/README.md); si no, explique en la PR por qué es solo para Apple.Esta es la única parte del discurso que no está terminada, y está escrita
a propósito (docs/project/MAD_STRATEGY.md §4.7): la capa de ejecución
tiene forma de driver —executeSQL:…intoResultTable:… y el
destino MADDataTableWriting son honestos sobre «cualquier cosa que devuelva
una tabla»—, pero la capa de compilación es compartida: MADQueryFull compila
el grafo de consultas en un texto SQL por driver. Su driver recibe texto
SQL, no el grafo (tablas, campos, predicados, grupos).
Así que hoy un driver REST/servicio web:
getTables…, fieldsForTable:…), tomar la
forma de conexión que la aplicación ya tiene, recibir el SQL generado y
traducirlo a llamadas de API, entregar filas a cualquier tabla escribible y
recorrer toda la ruta de actualización/renderizado/exportación: un driver totalmente funcional;docs/project/MAD_SQL_ENTRY.md) ya analiza el
subconjunto admitido —el SQL generado es ese subconjunto por construcción—, así que
un driver puede reutilizarlo para recuperar el grafo;Una nota honesta más, para despliegues de servidor y no para drivers: un driver instalado por el usuario es código arbitrario dentro del proceso del servidor. El registro funciona hoy para drivers propios y de confianza; un mercado de drivers necesita firma, aislamiento en sandbox o aislamiento de procesos de trabajo antes de poder existir, y eso está diseñado en la API del servidor, no añadido a posteriori.
Proponga el driver mediante la plantilla de issue de propuesta de driver, siga la regla de las pruebas primero en CONTRIBUTING.md (prueba que falla, después código hacia el verde; fixtures de instantánea en lugar de generarlas con el código nuevo), y si el driver enlaza una nueva biblioteca de terceros, amplíe docs/project/THIRD_PARTY_LICENSES.md y las obligaciones de licencia con ella: el repositorio se toma ese archivo en serio.
Esta página es una traducción al español del original en inglés; en caso de discrepancia, prevalece la versión original en inglés.