nia-etl-utils 0.1.0__py3-none-any.whl → 0.2.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.
nia_etl_utils/database.py CHANGED
@@ -1,208 +1,309 @@
1
1
  """Módulo de conexão com bancos de dados PostgreSQL e Oracle.
2
2
 
3
- Fornece funções genéricas e wrappers de conveniência para estabelecer conexões
4
- com diferentes bancos de dados usando credenciais de variáveis de ambiente.
3
+ Fornece funções para estabelecer conexões com bancos de dados usando
4
+ configurações explícitas ou variáveis de ambiente.
5
+
6
+ Examples:
7
+ Conexão com configuração explícita:
8
+
9
+ >>> from nia_etl_utils import conectar_postgresql, PostgresConfig
10
+ >>> config = PostgresConfig(
11
+ ... host="localhost",
12
+ ... port="5432",
13
+ ... database="meu_banco",
14
+ ... user="usuario",
15
+ ... password="senha"
16
+ ... )
17
+ >>> with conectar_postgresql(config) as conn:
18
+ ... conn.cursor.execute("SELECT * FROM tabela")
19
+ ... dados = conn.cursor.fetchall()
20
+
21
+ Conexão com variáveis de ambiente:
22
+
23
+ >>> config = PostgresConfig.from_env("_OPENGEO")
24
+ >>> with conectar_postgresql(config) as conn:
25
+ ... conn.cursor.execute("SELECT 1")
26
+
27
+ Wrappers de conveniência:
28
+
29
+ >>> with conectar_postgresql_nia() as conn:
30
+ ... conn.cursor.execute("SELECT * FROM ouvidorias")
5
31
  """
6
- import sys
32
+
7
33
  import cx_Oracle
8
34
  import psycopg2
9
35
  from loguru import logger
10
36
  from psycopg2 import Error, OperationalError
11
- from sqlalchemy import create_engine
37
+ from sqlalchemy import create_engine, text
12
38
  from sqlalchemy.engine import Engine
13
39
 
14
- from .env_config import obter_variavel_env
40
+ from .config import OracleConfig, PostgresConfig
41
+ from .exceptions import ConexaoError
42
+ from .results import Conexao
43
+
15
44
 
45
+ def conectar_postgresql(config: PostgresConfig) -> Conexao:
46
+ """Estabelece conexão com banco PostgreSQL.
16
47
 
17
- def conectar_postgresql(sufixo_env: str = "") -> tuple:
18
- """Estabelece conexão genérica com banco de dados PostgreSQL usando psycopg2.
48
+ Cria uma conexão usando psycopg2 com os parâmetros fornecidos
49
+ na configuração. A conexão retornada suporta context manager
50
+ para fechamento automático.
19
51
 
20
52
  Args:
21
- sufixo_env: Sufixo das variáveis de ambiente para conexão.
22
- Vazio ("") para banco padrão (DB_POSTGRESQL_*).
23
- "_OPENGEO" para OpenGeo (DB_POSTGRESQL_HOST_OPENGEO, etc).
53
+ config: Configuração de conexão PostgreSQL contendo host,
54
+ port, database, user e password.
24
55
 
25
56
  Returns:
26
- tuple: (cursor, connection) — cursor e objeto de conexão ativa.
57
+ Conexao com cursor e connection ativos, pronta para uso.
27
58
 
28
59
  Raises:
29
- SystemExit: Se houver erro ao estabelecer a conexão.
60
+ ConexaoError: Se houver falha ao estabelecer a conexão,
61
+ seja por credenciais inválidas, host inacessível ou
62
+ outros problemas de conectividade.
30
63
 
31
64
  Examples:
32
- >>> # Conecta usando variáveis DB_POSTGRESQL_HOST, DB_POSTGRESQL_PORT, etc
33
- >>> cur, conn = conectar_postgresql()
34
- >>> cur.execute("SELECT * FROM tabela")
35
- >>> resultados = cur.fetchall()
36
- >>> cur.close()
37
- >>> conn.close()
38
-
39
- >>> # Conecta em OpenGeo usando DB_POSTGRESQL_HOST_OPENGEO, etc
40
- >>> cur, conn = conectar_postgresql("_OPENGEO")
65
+ Com configuração explícita:
66
+
67
+ >>> config = PostgresConfig(
68
+ ... host="localhost",
69
+ ... port="5432",
70
+ ... database="teste",
71
+ ... user="user",
72
+ ... password="pass"
73
+ ... )
74
+ >>> with conectar_postgresql(config) as conn:
75
+ ... conn.cursor.execute("SELECT 1")
76
+ ... resultado = conn.cursor.fetchone()
77
+ ... print(resultado)
78
+ (1,)
79
+
80
+ Com variáveis de ambiente:
81
+
82
+ >>> config = PostgresConfig.from_env("_OPENGEO")
83
+ >>> with conectar_postgresql(config) as conn:
84
+ ... conn.cursor.execute("SELECT COUNT(*) FROM tabela")
85
+
86
+ Sem context manager (requer fechamento manual):
87
+
88
+ >>> conn = conectar_postgresql(config)
89
+ >>> try:
90
+ ... conn.cursor.execute("SELECT * FROM usuarios")
91
+ ... usuarios = conn.cursor.fetchall()
92
+ ... finally:
93
+ ... conn.fechar()
41
94
  """
42
- try:
43
- host = obter_variavel_env(f"DB_POSTGRESQL_HOST{sufixo_env}")
44
- port = obter_variavel_env(f"DB_POSTGRESQL_PORT{sufixo_env}")
45
- database = obter_variavel_env(f"DB_POSTGRESQL_DATABASE{sufixo_env}")
46
- user = obter_variavel_env(f"DB_POSTGRESQL_USER{sufixo_env}")
47
- password = obter_variavel_env(f"DB_POSTGRESQL_PASSWORD{sufixo_env}")
48
-
49
- logger.info(f"Iniciando conexão PostgreSQL em {host}:{port}, database '{database}'...")
95
+ logger.info(
96
+ f"Conectando PostgreSQL em {config.host}:{config.port}, "
97
+ f"database '{config.database}'..."
98
+ )
50
99
 
100
+ try:
51
101
  connection = psycopg2.connect(
52
- host=host,
53
- port=port,
54
- database=database,
55
- user=user,
56
- password=password
102
+ host=config.host,
103
+ port=config.port,
104
+ database=config.database,
105
+ user=config.user,
106
+ password=config.password
57
107
  )
58
108
  cursor = connection.cursor()
59
109
 
60
- logger.success(f"Conexão PostgreSQL estabelecida: '{database}' em '{host}:{port}'")
61
- return cursor, connection
110
+ logger.success(
111
+ f"Conexão PostgreSQL estabelecida: '{config.database}' "
112
+ f"em '{config.host}:{config.port}'"
113
+ )
114
+
115
+ return Conexao(cursor=cursor, connection=connection, database=config.database)
62
116
 
63
- except OperationalError as error:
64
- logger.error(f"Erro operacional PostgreSQL (sufixo: '{sufixo_env}'): {error}")
65
- sys.exit(1)
66
- except Error as error:
67
- logger.error(f"Erro PostgreSQL (sufixo: '{sufixo_env}'): {error}")
68
- sys.exit(1)
69
- except Exception as error:
70
- logger.error(f"Erro inesperado PostgreSQL (sufixo: '{sufixo_env}'): {error}")
71
- sys.exit(1)
117
+ except OperationalError as e:
118
+ raise ConexaoError(
119
+ f"Erro operacional PostgreSQL ({config.database})",
120
+ details={"host": config.host, "port": config.port, "erro": str(e)}
121
+ ) from e
122
+ except Error as e:
123
+ raise ConexaoError(
124
+ f"Erro PostgreSQL ({config.database})",
125
+ details={"host": config.host, "port": config.port, "erro": str(e)}
126
+ ) from e
72
127
 
73
128
 
74
- def obter_engine_postgresql(sufixo_env: str = "") -> Engine:
75
- """Cria uma engine SQLAlchemy genérica para PostgreSQL.
129
+ def conectar_oracle(config: OracleConfig) -> Conexao:
130
+ """Estabelece conexão com banco Oracle.
131
+
132
+ Cria uma conexão usando cx_Oracle com os parâmetros fornecidos
133
+ na configuração. A conexão retornada suporta context manager
134
+ para fechamento automático.
76
135
 
77
136
  Args:
78
- sufixo_env: Sufixo das variáveis de ambiente para conexão.
79
- Vazio ("") para banco padrão, "_OPENGEO" para OpenGeo.
137
+ config: Configuração de conexão Oracle contendo host,
138
+ port, service_name, user e password.
80
139
 
81
140
  Returns:
82
- Engine configurada para PostgreSQL.
141
+ Conexao com cursor e connection ativos, pronta para uso.
83
142
 
84
143
  Raises:
85
- SystemExit: Se houver erro na criação da conexão.
144
+ ConexaoError: Se houver falha ao estabelecer a conexão.
86
145
 
87
146
  Examples:
88
- >>> import pandas as pd
89
- >>> engine = obter_engine_postgresql()
90
- >>> df = pd.read_sql("SELECT * FROM tabela", engine)
91
-
92
- >>> # Engine para OpenGeo
93
- >>> engine = obter_engine_postgresql("_OPENGEO")
147
+ Com configuração explícita:
148
+
149
+ >>> config = OracleConfig(
150
+ ... host="oracle.empresa.com",
151
+ ... port="1521",
152
+ ... service_name="PROD",
153
+ ... user="user",
154
+ ... password="pass"
155
+ ... )
156
+ >>> with conectar_oracle(config) as conn:
157
+ ... conn.cursor.execute("SELECT * FROM tabela WHERE ROWNUM <= 10")
158
+
159
+ Com variáveis de ambiente:
160
+
161
+ >>> config = OracleConfig.from_env()
162
+ >>> with conectar_oracle(config) as conn:
163
+ ... conn.cursor.execute("SELECT SYSDATE FROM DUAL")
94
164
  """
165
+ logger.info(
166
+ f"Conectando Oracle em {config.host}:{config.port}, "
167
+ f"service '{config.service_name}'..."
168
+ )
169
+
95
170
  try:
96
- host = obter_variavel_env(f"DB_POSTGRESQL_HOST{sufixo_env}")
97
- port = obter_variavel_env(f"DB_POSTGRESQL_PORT{sufixo_env}")
98
- database = obter_variavel_env(f"DB_POSTGRESQL_DATABASE{sufixo_env}")
99
- user = obter_variavel_env(f"DB_POSTGRESQL_USER{sufixo_env}")
100
- password = obter_variavel_env(f"DB_POSTGRESQL_PASSWORD{sufixo_env}")
171
+ connection = cx_Oracle.connect(
172
+ user=config.user,
173
+ password=config.password,
174
+ dsn=config.dsn
175
+ )
176
+ cursor = connection.cursor()
101
177
 
102
- connection_string = f"postgresql+psycopg2://{user}:{password}@{host}:{port}/{database}"
103
- engine = create_engine(connection_string)
178
+ logger.success(
179
+ f"Conexão Oracle estabelecida: '{config.service_name}' "
180
+ f"em '{config.host}:{config.port}'"
181
+ )
104
182
 
105
- # Testa a conexão
106
- with engine.connect() as conn:
107
- conn.execute("SELECT 1") # type: ignore
183
+ return Conexao(cursor=cursor, connection=connection, database=config.service_name)
108
184
 
109
- logger.success(f"Engine PostgreSQL criada: '{database}' em '{host}:{port}'")
110
- return engine
185
+ except cx_Oracle.DatabaseError as e:
186
+ raise ConexaoError(
187
+ f"Erro de banco Oracle ({config.service_name})",
188
+ details={"host": config.host, "port": config.port, "erro": str(e)}
189
+ ) from e
190
+ except cx_Oracle.InterfaceError as e:
191
+ raise ConexaoError(
192
+ f"Erro de interface Oracle ({config.service_name})",
193
+ details={"host": config.host, "port": config.port, "erro": str(e)}
194
+ ) from e
111
195
 
112
- except OperationalError as error:
113
- logger.error(f"Erro operacional ao criar engine PostgreSQL (sufixo: '{sufixo_env}'): {error}")
114
- sys.exit(1)
115
- except Exception as error:
116
- logger.error(f"Erro inesperado ao criar engine PostgreSQL (sufixo: '{sufixo_env}'): {error}")
117
- sys.exit(1)
118
196
 
197
+ def obter_engine_postgresql(config: PostgresConfig) -> Engine:
198
+ """Cria engine SQLAlchemy para PostgreSQL.
119
199
 
120
- def conectar_oracle() -> tuple:
121
- """Estabelece conexão com banco de dados Oracle.
200
+ Cria e testa uma engine SQLAlchemy usando a configuração fornecida.
201
+ A engine é útil para operações com pandas (read_sql, to_sql).
122
202
 
123
- Usa variáveis de ambiente: DB_ORACLE_HOST, DB_ORACLE_PORT, DB_ORACLE_SERVICE_NAME,
124
- DB_ORACLE_USER, DB_ORACLE_PASSWORD.
203
+ Args:
204
+ config: Configuração de conexão PostgreSQL.
125
205
 
126
206
  Returns:
127
- tuple: (cursor, connection) — cursor e objeto de conexão ativa.
207
+ Engine SQLAlchemy configurada e testada.
128
208
 
129
209
  Raises:
130
- SystemExit: Se houver erro ao estabelecer a conexão.
210
+ ConexaoError: Se houver falha ao criar ou testar a engine.
131
211
 
132
212
  Examples:
133
- >>> cur, conn = conectar_oracle()
134
- >>> cur.execute("SELECT * FROM tabela WHERE ROWNUM <= 10")
135
- >>> resultados = cur.fetchall()
136
- >>> fechar_conexao(cur, conn)
213
+ Leitura com pandas:
214
+
215
+ >>> import pandas as pd
216
+ >>> config = PostgresConfig.from_env()
217
+ >>> engine = obter_engine_postgresql(config)
218
+ >>> df = pd.read_sql("SELECT * FROM tabela", engine)
219
+
220
+ Escrita com pandas:
221
+
222
+ >>> df.to_sql("nova_tabela", engine, if_exists="replace", index=False)
137
223
  """
138
224
  try:
139
- host = obter_variavel_env("DB_ORACLE_HOST")
140
- port = obter_variavel_env("DB_ORACLE_PORT")
141
- service_name = obter_variavel_env("DB_ORACLE_SERVICE_NAME")
142
- user = obter_variavel_env("DB_ORACLE_USER")
143
- password = obter_variavel_env("DB_ORACLE_PASSWORD")
225
+ engine = create_engine(config.connection_string)
144
226
 
145
- logger.info(f"Iniciando conexão Oracle em {host}:{port}, service '{service_name}'...")
227
+ # Testa a conexão
228
+ with engine.connect() as conn:
229
+ conn.execute(text("SELECT 1"))
146
230
 
147
- dsn = cx_Oracle.makedsn(host, port, service_name=service_name)
148
- connection = cx_Oracle.connect(user=user, password=password, dsn=dsn)
149
- cursor = connection.cursor()
231
+ logger.success(
232
+ f"Engine PostgreSQL criada: '{config.database}' "
233
+ f"em '{config.host}:{config.port}'"
234
+ )
235
+ return engine
150
236
 
151
- logger.success(f"Conexão Oracle estabelecida: '{service_name}' em '{host}:{port}'")
152
- return cursor, connection
237
+ except OperationalError as e:
238
+ raise ConexaoError(
239
+ f"Erro ao criar engine PostgreSQL ({config.database})",
240
+ details={"host": config.host, "port": config.port, "erro": str(e)}
241
+ ) from e
153
242
 
154
- except cx_Oracle.DatabaseError as error:
155
- logger.error(f"Erro de banco Oracle: {error}")
156
- sys.exit(1)
157
- except cx_Oracle.InterfaceError as error:
158
- logger.error(f"Erro de interface Oracle: {error}")
159
- sys.exit(1)
160
- except Exception as error:
161
- logger.error(f"Erro inesperado Oracle: {error}")
162
- sys.exit(1)
163
243
 
244
+ # =============================================================================
245
+ # WRAPPERS DE CONVENIÊNCIA
246
+ # =============================================================================
164
247
 
165
- def fechar_conexao(cursor, connection) -> None:
166
- """Encerra cursor e conexão de banco de dados de forma segura.
167
248
 
168
- Funciona para conexões PostgreSQL (psycopg2) e Oracle (cx_Oracle).
249
+ def conectar_postgresql_nia() -> Conexao:
250
+ """Conecta no PostgreSQL do NIA usando variáveis de ambiente padrão.
169
251
 
170
- Args:
171
- cursor: Cursor ativo que será fechado.
172
- connection: Conexão ativa que será encerrada.
252
+ Usa as variáveis: DB_POSTGRESQL_HOST, DB_POSTGRESQL_PORT,
253
+ DB_POSTGRESQL_DATABASE, DB_POSTGRESQL_USER, DB_POSTGRESQL_PASSWORD.
254
+
255
+ Returns:
256
+ Conexao configurada para o banco NIA.
257
+
258
+ Raises:
259
+ ConexaoError: Se houver falha ao conectar.
260
+ ConfiguracaoError: Se variáveis de ambiente estiverem ausentes.
173
261
 
174
262
  Examples:
175
- >>> cur, conn = conectar_postgresql()
176
- >>> # ... usar cursor ...
177
- >>> fechar_conexao(cur, conn)
263
+ >>> with conectar_postgresql_nia() as conn:
264
+ ... conn.cursor.execute("SELECT * FROM ouvidorias LIMIT 10")
265
+ ... dados = conn.cursor.fetchall()
178
266
  """
179
- try:
180
- if cursor:
181
- cursor.close()
182
- logger.debug("Cursor fechado com sucesso.")
267
+ return conectar_postgresql(PostgresConfig.from_env())
183
268
 
184
- if connection:
185
- connection.close()
186
- logger.debug("Conexão encerrada com sucesso.")
187
269
 
188
- except Exception as error:
189
- logger.warning(f"Erro ao fechar conexão: {error}")
190
- # Não usa sys.exit(1) porque fechar conexão é cleanup
270
+ def conectar_postgresql_opengeo() -> Conexao:
271
+ """Conecta no PostgreSQL OpenGeo usando variáveis de ambiente.
191
272
 
273
+ Usa as variáveis com sufixo _OPENGEO: DB_POSTGRESQL_HOST_OPENGEO, etc.
192
274
 
193
- # =============================================================================
194
- # WRAPPERS DE CONVENIÊNCIA - APIs específicas para bancos conhecidos
195
- # =============================================================================
275
+ Returns:
276
+ Conexao configurada para o banco OpenGeo.
277
+
278
+ Raises:
279
+ ConexaoError: Se houver falha ao conectar.
280
+ ConfiguracaoError: Se variáveis de ambiente estiverem ausentes.
281
+
282
+ Examples:
283
+ >>> with conectar_postgresql_opengeo() as conn:
284
+ ... conn.cursor.execute("SELECT * FROM geo_data")
285
+ """
286
+ return conectar_postgresql(PostgresConfig.from_env("_OPENGEO"))
196
287
 
197
- def conectar_postgresql_nia() -> tuple:
198
- """Conecta no PostgreSQL do NIA.
199
288
 
200
- Usa variáveis de ambiente: DB_POSTGRESQL_HOST, DB_POSTGRESQL_PORT, etc.
289
+ def conectar_oracle_ouvidorias() -> Conexao:
290
+ """Conecta no Oracle de Ouvidorias usando variáveis de ambiente.
291
+
292
+ Usa as variáveis: DB_ORACLE_HOST, DB_ORACLE_PORT,
293
+ DB_ORACLE_SERVICE_NAME, DB_ORACLE_USER, DB_ORACLE_PASSWORD.
201
294
 
202
295
  Returns:
203
- tuple: (cursor, connection)
296
+ Conexao configurada para o Oracle.
297
+
298
+ Raises:
299
+ ConexaoError: Se houver falha ao conectar.
300
+ ConfiguracaoError: Se variáveis de ambiente estiverem ausentes.
301
+
302
+ Examples:
303
+ >>> with conectar_oracle_ouvidorias() as conn:
304
+ ... conn.cursor.execute("SELECT * FROM SGOV.OUVIDORIAS")
204
305
  """
205
- return conectar_postgresql("")
306
+ return conectar_oracle(OracleConfig.from_env())
206
307
 
207
308
 
208
309
  def obter_engine_postgresql_nia() -> Engine:
@@ -210,19 +311,17 @@ def obter_engine_postgresql_nia() -> Engine:
210
311
 
211
312
  Returns:
212
313
  Engine configurada para o banco NIA.
213
- """
214
- return obter_engine_postgresql("")
215
-
216
-
217
- def conectar_postgresql_opengeo() -> tuple:
218
- """Conecta no PostgreSQL OpenGeo.
219
314
 
220
- Usa variáveis de ambiente: DB_POSTGRESQL_HOST_OPENGEO, DB_POSTGRESQL_PORT_OPENGEO, etc.
315
+ Raises:
316
+ ConexaoError: Se houver falha ao criar engine.
317
+ ConfiguracaoError: Se variáveis de ambiente estiverem ausentes.
221
318
 
222
- Returns:
223
- tuple: (cursor, connection)
319
+ Examples:
320
+ >>> import pandas as pd
321
+ >>> engine = obter_engine_postgresql_nia()
322
+ >>> df = pd.read_sql("SELECT * FROM tabela", engine)
224
323
  """
225
- return conectar_postgresql("_OPENGEO")
324
+ return obter_engine_postgresql(PostgresConfig.from_env())
226
325
 
227
326
 
228
327
  def obter_engine_postgresql_opengeo() -> Engine:
@@ -230,17 +329,14 @@ def obter_engine_postgresql_opengeo() -> Engine:
230
329
 
231
330
  Returns:
232
331
  Engine configurada para o banco OpenGeo.
233
- """
234
- return obter_engine_postgresql("_OPENGEO")
235
-
236
-
237
- def conectar_oracle_ouvidorias() -> tuple:
238
- """Conecta no Oracle de Ouvidorias.
239
332
 
240
- Alias para conectar_oracle() — mantido para compatibilidade semântica.
241
- Usa variáveis de ambiente: DB_ORACLE_HOST, DB_ORACLE_PORT, etc.
333
+ Raises:
334
+ ConexaoError: Se houver falha ao criar engine.
335
+ ConfiguracaoError: Se variáveis de ambiente estiverem ausentes.
242
336
 
243
- Returns:
244
- tuple: (cursor, connection)
337
+ Examples:
338
+ >>> import pandas as pd
339
+ >>> engine = obter_engine_postgresql_opengeo()
340
+ >>> df = pd.read_sql("SELECT * FROM geo_tabela", engine)
245
341
  """
246
- return conectar_oracle()
342
+ return obter_engine_postgresql(PostgresConfig.from_env("_OPENGEO"))