tag:blogger.com,1999:blog-80406371174852066492024-03-05T15:20:15.704-05:00BitExperienceTips personales sobre Linux y programación web usando PHP, SQL y SymfonyFabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comBlogger46125tag:blogger.com,1999:blog-8040637117485206649.post-29689956878554927592015-08-28T20:02:00.001-05:002015-08-28T20:02:08.948-05:00Retorno de funciones de Trigger ejecutadas antes de operaciones DELETE en PostgreSQLAl ejecutar una función de trigger antes de operaciones DELETE no debe olvidarse el retorno de la función.<br />
<br />
RETURN NULL hace que se salte la ejecución de la operación por lo que no se realizará el borrado de los registros. De modo que si queremos que efectivamente se realice la operación DELETE el retorno debe ser algo diferente de NULL.<a name='more'></a><br />
<br />
RETURN NEW funciona del mismo modo que RETURN NULL puesto que para las operaciones DELETE estas dos sentencias son equivalentes.<br />
<br />
RETURN OLD es una forma de permitir que se ejecute la operación de forma normal, aunque cualquier otro valor debe funcionar, sin embargo esta es la forma más aceptada.Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-27932821758361332172015-01-09T19:18:00.002-05:002015-01-09T19:28:35.386-05:00Enviar correos usando SMTP con una cuenta de GmailSi queremos hacer uso de una cuenta de Google para enviar correos usando SMTP (ej: Al enviar correos automatizados en una aplicación web), hay algunos pasos que debemos seguir:<br />
<br />
<ol><li><b>Dar los valores de configuración del servidor de Google en nuestra aplicación:</b> Estos valores son:<br />
<ul><li>Servidor SMTP (host): smtp.gmail.com</li>
<li>Puerto: 465</li>
<li>Usuario: Tu usuario de gmail (ej: miusuario@gmail.com)</li>
<li>Contraseña: Tu contraseña</li>
</ul>Es probable que incluso con estos datos no podamos enviar correos y obtengamos un error como:<br />
<code>Connection to smtp.gmail.com:465 Timed Out</code><br />
Este error se debe a que no se reconoce correctamente el tipo de encriptación.<a name='more'></a><br />
</li>
<li><b>Definir el tipo de encriptación:</b> Dependiendo de qué aplicación estemos usando es probable que necesitemos definir esta variable. El valor debe ser <b>sslv3</b>.<br />
Incluso después de esto podriamos obtener un error como el siguiente al tratar de enviar un correo.<br />
<code>Failed to authenticate on SMTP server with username "miusuario@gmail.com" using 2 possible authenticators</code><br />
Esto puede suceder como cuando estamos enviando los correos a través de un script PHP.<br />
</li>
<li><b>Configurar la política de seguridad en nuestra cuenta de gmail:</b> Luego de haber intentado enviar el correo, seguramente recibiremos un correo en nuestra cuenta de gmail informándonos de un <b>intento de inicio de sesión bloqueado</b>. En este correo se describe desde donde se intentó hacer el ingreso y nos dan instrucciones para permitir el inicio de sesión en caso de que no sea un intento fraudulento. Lo que se debe hacer es ir a Cuenta -> Acceso de aplicaciones menos seguras. Allí habilitamos el acceso y Listo!</li>
</ol>Con esto ya deberíamos poder enviar correos desde nuestra aplicación usando SMTP con una cuenta de Google. <br />
<ol></ol>Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-78254717895228573632012-12-07T15:43:00.003-05:002012-12-10T09:31:44.340-05:00Consideraciones al renombrar una tabla en PostgreSQLRenombrar una tabla es algo sencillo, simplemente hacemos uso del comando ALTER TABLE:<br />
<code><br />
ALTER TABLE nombre_actual RENAME TO nuevo_nombre;<br />
</code><br />
Sin embargo, este comando sólo renombra la tabla mientras los nombres de sus índices, secuencias, llaves foráneas y otros constraints permanecen iguales. Esto podría no ser un problema, pero si estos nombres son parte del diseño de la BD y queremos cambiarlos, deberemos renombrar los índices y secuencias uno por uno:<a name='more'></a><br />
<code><br />
ALTER INDEX nombre_actual_pkey RENAME TO nuevo_nombre_pkey;<br />
ALTER SEQUENCE nombre_actual_id_seq RENAME TO nuevo_nombre_id_seq;<br />
</code><br />
y recrear las llaves foráneas y constraints:<br />
<code><br />
ALTER TABLE nombre_actual DROP CONSTRAINT nombre_actual_campo_fkey;<br />
ALTER TABLE nombre_actual ADD CONSTRAINT nuevo_nombre_campo_fkey REFERENCES otra_tabla(id);<br />
</code><br />
Otra opción es hacer un backup de la tabla, eliminarla, recrearla con los nombres que deseamos y cargar el backup de datos.<br />
<br />
Ahora, aunque el renombrar la tabla no renombra sus lláves foráneas SÍ cambia la definición de llaves foráneas que dependan de la tabla renombrada, por lo que se conserva la integridad referencial. Por ejemplo si tenemos la tabla 'otra_tabla' que tiene una llave foránea al campo 'nombre_actual.id', al renombrar esta tabla la llave foránea dentro de 'otra_tabla' es redefinida para referenciar al campo 'nuevo_nombre.id'.<br />
<br />
Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-34734486488923008462012-12-05T17:02:00.000-05:002012-12-11T16:28:48.067-05:00Evitar errores en búsquedas sobre campos de texto con mayúsculas y minúsculas en PostgreSQLSi los campos de texto se almacenan con mezclas de mayúsculas y minúsculas, las búsquedas se ven afectadas y podrían no entregar los resultados que esperamos. Para ilustrarlo supongamos que tenemos una tabla llamada 'persona' con un campo 'nombre', en el que almacenaremos la información introducida por un usuario mediante un formulario y que no procesaremos dicho campo, por lo que el usuario podrá ingresar el texto usando mayúsculas y minúsculas. Luego de varios ingresos tendremos los siguientes datos:<a name='more'></a><br />
<code><br />
SELECT id, nombre FROM prueba_persona;<br />
id | nombre <br />
----+---------------------<br />
1 | CARLOS PEREZ<br />
2 | Carmen Pereira<br />
3 | Ricardo Gonzalez<br />
4 | gonzalo Perez<br />
5 | PERICLES RICAURTE<br />
6 | perico portocarrero<br />
(6 filas)<br />
</code><br />
Con estos datos, si quisiéramos obtener los nombres que contengan la cadena 'car' esperaríamos obtener las filas 1, 2, 3 y 6, pero si efectuamos la consulta obtendremos lo siguiente:<br />
<code><br />
SELECT id, nombre FROM persona WHERE nombre LIKE '%car%';<br />
id | nombre <br />
----+---------------------<br />
3 | Ricardo Gonzalez<br />
6 | perico portocarrero<br />
(2 filas)<br />
</code><br />
Porque el motor de Bases de Datos distingue entre mayúsculas y minúsculas como caracteres diferentes. Una solución rápida consiste en modificar la consulta así:<br />
<code><br />
SELECT id, nombre FROM persona WHERE lower(nombre) LIKE '%car%';<br />
id | nombre <br />
----+---------------------<br />
1 | CARLOS PEREZ<br />
2 | Carmen Pereira<br />
3 | Ricardo Gonzalez<br />
6 | perico portocarrero<br />
(4 filas)<br />
</code><br />
En esta consulta estamos primero pasando el atributo a minúsculas y sobre el texto modificado efectuamos la consulta. De este modo podremos obtener los datos que realmente buscamos. Hay que aclarar que no se modifican los valores del atributo dentro de la tabla sino en el búfer de búsqueda.<br />
<br />
Sin embargo, si hacemos un uso recurrente de este tipo de consultas, es mejor usar un método más eficiente. Si no importa la mezcla de minúsculas y mayúsculas podemos modificar el valor del campo en un trigger antes de hacer el INSERT y almacenar el texto en minúsculas:<br />
<code><br />
CREATE OR REPLACE FUNCTION modificar_nombre_persona() RETURNS TRIGGER AS $$<br />
BEGIN<br />
<br />
NEW.nombre := lower(NEW.nombre);<br />
RETURN NEW;<br />
<br />
END<br />
$$<br />
LANGUAGE 'plpgsql';<br />
<br />
CREATE TRIGGER persona_antes_insertar BEFORE INSERT ON persona FOR EACH ROW EXECUTE PROCEDURE modificar_nombre_persona();<br />
</code><br />
De este modo al ingresar los datos éstos quedarán almacenados así:<br />
<code><br />
SELECT id, nombre_busquedas FROM prueba_persona;<br />
id | nombre_busquedas <br />
----+---------------------<br />
1 | carlos perez<br />
2 | carmen pereira<br />
3 | ricardo gonzalez<br />
4 | gonzalo perez<br />
5 | pericles ricaurte<br />
6 | perico portocarrero<br />
(6 filas)<br />
</code><br />
Y al hacer la misma búsqueda obtendremos los siguiente resultados:<br />
<code><br />
SELECT id, nombre FROM persona WHERE nombre LIKE '%car%';<br />
id | nombre_busquedas <br />
----+---------------------<br />
1 | carlos perez<br />
2 | carmen pereira<br />
3 | ricardo gonzalez<br />
6 | perico portocarrero<br />
(4 filas)<br />
</code><br />
Pero si es necesario conservar la forma escrita por el usuario, podemos crear un campo adicional, en el que almacenaremos el valor en minúsculas y no alteraremos el campo original.<br />
<code><br />
ALTER TABLE persona ADD nombre_busquedas varchar(255);<br />
<br />
CREATE OR REPLACE FUNCTION crear_nombre_busquedas_persona() RETURNS TRIGGER AS $$<br />
BEGIN<br />
<br />
NEW.nombre_busquedas := lower(NEW.nombre);<br />
RETURN NEW;<br />
<br />
END<br />
$$<br />
LANGUAGE 'plpgsql';<br />
<br />
CREATE TRIGGER persona_antes_insertar BEFORE INSERT ON persona FOR EACH ROW EXECUTE PROCEDURE crear_nombre_busquedas_persona();<br />
</code><br />
En este caso los datos quedarían almacenados así:<br />
<code><br />
SELECT * FROM persona;<br />
id | nombre | nombre_busquedas <br />
----+---------------------+---------------------<br />
1 | CARLOS PEREZ | carlos perez<br />
2 | Carmen Pereira | carmen pereira<br />
3 | Ricardo Gonzalez | ricardo gonzalez<br />
4 | gonzalo Perez | gonzalo perez<br />
5 | PERICLES RICAURTE | pericles ricaurte<br />
6 | perico portocarrero | perico portocarrero<br />
(6 filas)<br />
</code><br />
Y la consulta se haría así:<br />
<code><br />
SELECT id, nombre FROM persona WHERE nombre_busquedas LIKE '%car%';<br />
id | nombre <br />
----+---------------------<br />
1 | CARLOS PEREZ<br />
2 | Carmen Pereira<br />
3 | Ricardo Gonzalez<br />
6 | perico portocarrero<br />
(4 filas)<br />
</code><br />
Mediante cualquiera de estas dos formas evitaremos el uso de funciones adicionales en la consulta y obtendremos un poco más de eficiencia.<br />
<br />
Eso es todo!<br />
<br />
PDTA: También habría que ejecutar los triggers antes de actualizar.<br />
<br />
Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-11155943195791537412012-10-05T16:52:00.001-05:002012-12-10T09:32:06.397-05:00Error al cargar backup de PostgreSQL 9.1 a 8.4 con funciones que usen PL/pgsqlAl querer cargar un archivo de backup desde una versión más reciente a una anterior siempre surgen problemas. Este es uno de ellos si cargamos un archivo de backup hecho con pg_dump desde la versión 9.1 a una versión anterior y tenemos funciones (tal vez triggers) programados con PL/pgsql. A mí me sucede porque mi máquina de trabajo tiene un Fedora 17 y mi servidor tiene Debian 6. Al intentarlo aparecerá el siguiente error:<br />
<code><br />
ERROR: error de sintaxis en o cerca de «EXTENSION»<br />
LÍNEA 1: CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalo...<br />
</code><br />
Y no creará las funciones que dependan de este lenguaje.<br />
<a name='more'></a><br />
Ahora PostgreSql introdujo el comando CREATE EXTENSION para reemplazar varias tareas. Este comando no existe en las versiones anteriores a la 9.1, pero si no observamos más errores, corregirlo es sencillo: editamos nuestro archivo de backup y eliminamos las siguientes líneas:<br />
<code><br />
CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;<br />
COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';<br />
</code><br />
Y las reemplazamos por la siguiente:<br />
<code><br />
CREATE LANGUAGE 'plpgsql' HANDLER plpgsql_call_handler LANCOMPILER 'PL/pgSQL';<br />
</code><br />
Y listo! Claro está que ésto sólo corregirá el error para PL/pgSQL y si tenemos más problemas de compatibilidad con CREATE EXTENSION habrá que buscar la solución para cada uno de ellos.Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-10095761429394749282012-09-25T19:06:00.000-05:002012-09-25T19:06:07.434-05:00Extender la capacidad del historial de la consola linuxEl valor por defecto del número de líneas del historial de la consola depende de la distribución pero por lo general es de 500 líneas. Si queremos aumentar ese número lo que haremos será editar el archivo .bashrc situado en la raíz del directorio de nuestro usuario y agregar la siguiente línea al final del archivo:<br />
<code><br />
# bash history debería guardar 3000 comandos:<br />
export HISTFILESIZE=3000<br />
</code><br />
Además podemos optimizar esos comandos guardados, haciendo que se eliminen los comandos repetidos en forma consecutiva con la siguiente línea:<br />
<code><br />
# No colocar líneas repetidas en el historial:<br />
export HISTCONTROL=ignoredups<br />
</code><br />
Ahora, reiniciar la terminal y listo!Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-31282446984736506842012-09-21T18:13:00.003-05:002012-09-21T18:16:10.123-05:00Las extensiones de Gnome-shell imprescindibles para míEn el <a href="http://bitexperience.blogspot.com/2012/09/como-mejorar-gnome-shell-con-extensiones.html" target="_blank">post anterior</a> hablé de las <a href="https://extensions.gnome.org/" target="_blank">gnome-shell extensions</a>, la forma que nos ofrece Gnome de personalizar nuestro entorno gráfico. De paso quisiera agradecer desde acá a aquellos que se han tomado el tiempo de programar estas extensiones y devolvernos algo de lo que hemos perdido con el nuevo gnome-shell. <br />
<br />
Ahora sí daré el listado de mis extensiones favoritas e indispensables para mí:<br />
<a name='more'></a><br />
<ul>
<li>Alternative Status Menu: Muestra las opciones de Hibernar, Suspender y Apagar. Como tenía que ser.</li>
<li>Applications Menu: Agrega un menú de aplicaciones. Imprescindible. </li>
<li>Dash to Dock: Este me gusta bastante. Hace que podamos usar el dash con atajos de aplicaciones sin tener que entrar a la zona de aplicaciones. Aparece y se oculta automáticamente cada que movemos el ratón hacia el borde izquierdo de la pantalla.</li>
<li>Activities Configurator: Permite configurar el botón de Actividades, yo lo uso para desactivar la 'Hot Corner' que detesto :)</li>
<li>All-in-one Places: Crea un menú en la barra superior para acceder a dispositivos, lugares, documentos recientes, y más.</li>
<li>Remove Accessibility: Elimina el botón de accesibilidad.</li>
<li>Media player indicator: Información y controles básicos para reproductores multimedia en el botón de control de volumen.</li>
<li>SettingsCenter: Nos crea un menú con atajos para varias configuraciones: gnome-tweak-tool, dconf-editor, gconf-editor, gnome-session-properties, gnome-shell-extension-prefs, seahorse y nvidia-settings.</li>
<li>Impatience: Acelera la animación de gnome-shell.</li>
<li>SystemMonitor: Muestra los gráficos de desempeño de nuestro equipo y si le hacemos click lanzará el Monitor del Sistema.</li>
<li>Extension shortcuts: Crea un menú en la barra superior con atajos para configuración de las extensiones.</li>
<li>Force Quit: Crea una opción en la barra superior para forzar el cierre de una aplicación que no responda.</li>
<li>Transmission Daemon Indicator: Nos muestra información básica sobre nuestras descargas con Transmission, permite pausar e iniciar.</li>
</ul>
Algunas extensiones permiten configurar varias opciones según nuestras preferencias. Para hacerlo debemos acceder a la aplicación de preferencias de las extensiones (Extensions preferences), al cual podemos acceder fácilmente si instalamos las extensiones SettingsCenter o Extension shortcuts.<br />
<ul></ul>
Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-47966859329370706102012-09-21T17:44:00.001-05:002012-09-21T18:14:15.728-05:00Como mejorar Gnome-shell con extensionesCon el nuevo Gnome Shell tenemos un entorno gráfico atractivo pero no viene sin sus problemas y es que la gente de Gnome ha hecho algunas cosas que parecen no tener mucho sentido. Entre los cambios en la interfaz que no me gustan nada están:<br />
<br />
<ul><li>Quitar la opción de apagado, WTF!: Ahora sólo tenemos la opción de suspender y para ver la opción para apagar debemos oprimir la tecla Alt mientras pasamos el cursor sobre la opción suspender. Esto es simplemente absurdo!</li>
<li>La 'HotCorner': Es realmente molesto que cada vez que pasa el cursor sobre el texto Actividades automáticamente vemos el efecto que nos saca del escritorio para entrar a la zona de actividades. Y es que suele pasar por accidente y tenemos que volver al escritorio. Molesto!</li>
<li>¿Dónde está el menú de aplicaciones?: Si queremos usar alguna aplicación tenemos que ir a la zona de Actividades y allí escoger la opción de aplicaciones. ¿Acaso no había espacio en la barra superior? Otro WTF para los de gnome.</li>
</ul><br />
Afortunadamente para facilitarnos la vida están las extensiones de gnome-shell, con las que podemos mejorar nuestra experiencia de usuario con este entorno gráfico.<a name='more'></a> El modo más sencillo de instalarlas es usar el servicio web que nos ofrece Gnome <a href="https://extensions.gnome.org/" target="_blank">acá</a>. Desde allí podremos instalarlas, activarlas, desactivarlas y desinstalarlas.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeGKwM2K7aO2hQNrlIRxRno_05X300-eTGLdr_LxBFDOHExNqwiHFqYh6QZ3PrSh_0AUSjkFTdZDj5qYVwhL1XerOOik7W-sTcppbb6IMwT7uvCdDoSh3Nnm9Xb7E1ZfLkfKJ6sUkfCWBS/s1600/gnome_exts_home.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="gnome extensions homepage" border="0" height="347" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeGKwM2K7aO2hQNrlIRxRno_05X300-eTGLdr_LxBFDOHExNqwiHFqYh6QZ3PrSh_0AUSjkFTdZDj5qYVwhL1XerOOik7W-sTcppbb6IMwT7uvCdDoSh3Nnm9Xb7E1ZfLkfKJ6sUkfCWBS/s640/gnome_exts_home.png" title="" width="640" /></a></div><br />
Instalar una extensión es muy sencillo, simplemente la escogemos de la lista y en la página de la extensión oprimimos el botón que por defecto está en OFF.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsTl4pQgvgZ2au-wJDDSSeJz3YE2C_bxduzW8YqDBhvX0I_5VbVnC_0wZOxAlTq3hMCCPh3Mg6TfSS1EFbkLqr1LAiHlnw9-Sg2j_EJyvfFmj-FTpDJ5tNrvST5-HwfjOXjKR6ZDhEH_kn/s1600/gnome_exts_off.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="237" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsTl4pQgvgZ2au-wJDDSSeJz3YE2C_bxduzW8YqDBhvX0I_5VbVnC_0wZOxAlTq3hMCCPh3Mg6TfSS1EFbkLqr1LAiHlnw9-Sg2j_EJyvfFmj-FTpDJ5tNrvST5-HwfjOXjKR6ZDhEH_kn/s400/gnome_exts_off.png" width="400" /></a></div><br />
Al hacerlo se instalará la extensión en nuestro equipo y el botón aparecerá en ON.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1s8VAYx2WirKMKcn1AX7zzRXI_QbzDL96jqG_pNZ8HMtg1-JyaKRPHEg6lUJbzX0HOFQdZ25X3m5HC8VJQesnx_BS6BQqgEtu_vLL6axWM6UUwcTcF22JlpLVof2tugvkwhfKA57LcOs9/s1600/gnome_exts_on.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="238" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1s8VAYx2WirKMKcn1AX7zzRXI_QbzDL96jqG_pNZ8HMtg1-JyaKRPHEg6lUJbzX0HOFQdZ25X3m5HC8VJQesnx_BS6BQqgEtu_vLL6axWM6UUwcTcF22JlpLVof2tugvkwhfKA57LcOs9/s400/gnome_exts_on.png" width="400" /></a></div>Como ven es supremamente sencillo. Así que pueden navegar y probar las extensiones que les parezcan más atractivas. Algunas son muy útiles y nos devuelven opciones básicas. En otro artículo publicaré mis extensiones favoritas.Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-67456668554173241592012-07-13T17:36:00.000-05:002012-09-21T17:43:54.605-05:00Porqué usar llaves primarias autoincrementablesEn un post anterior explicaba una ventaja de los campos autoincrementables, pero además de ésta existen otras razones que hacen que sea una buena práctica incluir un campo autoincrementable como llave primaria (PK) en cada tabla.<br />
<br />
Incluir campos autoincrementables como llaves primarias en cada tabla de una base de datos tiene varias ventajas, que hacen que usarlas sea una buena práctica. Veamos algunas de esas buenas razones:<br />
<br />
<ol><li><b>Facilita la obtención del último registro insertado.</b><br />
<a name='more'></a><br />
Tener un campo autoincrementable en una tabla, hace mucho más sencillo este procedimiento en algunos casos, como ya lo expliqué en <a href="http://bitexperience.blogspot.com/2012/03/obtener-el-ultimo-registro-insertado-en.html">este post anterior</a>.<br />
<br />
</li>
<li><b>Permite conservar la unicidad de los registros mientras definimos o cambiamos el valor de un campo único.</b><br />
<br />
Supongamos que tenemos la tabla automovil, definida así:<br />
<code><br />
CREATE TABLE automovil (<br />
placa char(6) PRIMARY KEY,<br />
marca varchar(50),<br />
pasajeros int );<br />
</code><br />
Si por alguna razón nos encontramos con un valor incorrecto para la llave primaria de un nuevo registro, podemos igual hacer la inserción del registro y dejar pendiente la definición del valor para el campo placa si redefinimos la tabla así:<br />
<code><br />
CREATE TABLE automovil (<br />
id serial,<br />
placa char (6) UNIQUE,<br />
marca varchar(50),<br />
pasajeros int );<br />
</code><br />
Si no queremos permitir valores nulos para el campo placa tendríamos que definirlo como NOT NULL.<br />
<br />
</li>
<li><b>Permite conservar la integridad referencial entre dos o más tablas relacionadas.</b><br />
<br />
Siguiendo con el ejemplo anterior, supongamos que tenemos la tabla automovil relacionada con la tabla propietario, de la siguiente forma:<br />
<code><br />
CREATE TABLE propietario (<br />
documento_identificacion char(9) PRIMARY KEY,<br />
nombres varchar(100),<br />
apellidos varchar(100),<br />
direccion varchar(100),<br />
telefono varchar(100));<br />
<br />
CREATE TABLE automovil (<br />
placa char(6) PRIMARY KEY,<br />
marca varchar(50),<br />
pasajeros int <br />
propietario char(9) REFERENCES propietario(documento_identificacion));<br />
</code><br />
Si tuviéramos que cambiar el valor del campo documento_identificacion para un registro en propietario, tendríamos que cambiarlo también en automovil. Esto puede hacerse mediante el uso del comportamiento CASCADE en el evento ON UPDATE en la definición de la llave foránea, pero se estaría ejecutando una operación adicional en la base de datos por cada tabla que tenga una referencia con ese registro, y de hecho hay tablas que suelen tener muchas refencias, por lo que sería una carga adicional innecesaria que puede evitarse.<br />
<br />
Al redefinir las tablas usando llaves primarias autoincrementables podremos hacer cambios sobre los campos únicos sin tener que preocuparnos por la integridad referencial y las consultas adicionales:<br />
<code><br />
CREATE TABLE propietario (<br />
id serial PRIMARY KEY,<br />
documento_identificacion char(9) UNIQUE,<br />
nombres varchar(100),<br />
apellidos varchar(100),<br />
direccion varchar(100),<br />
telefono varchar(100));<br />
<br />
CREATE TABLE automovil (<br />
id PRIMARY KEY<br />
placa char(6) UNIQUE,<br />
marca varchar(50),<br />
pasajeros int <br />
propietario_id int REFERENCES propietario(id));<br />
</code><br />
</li>
</ol>Además de estas hay otras buenas razones, así que por qué no hacerlo?Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-34903724486826039962012-07-13T16:52:00.000-05:002012-07-13T16:52:03.332-05:00Instalación y arranque de PostgreSQL en Fedora 17Para usar el mejor servidor de Bases de Datos SQL en la nueva versión de Fedora deberemos llevar a cabo un par de sencillos pasos, que varían un poco respecto a la versión anterior, estos comandos habrá que ejecutarlos como root o mediante el comando sudo:<br />
<ul><li>Instalar:<br />
<code><br />
# yum install postgresql postgresql-server<br />
</code><br />
</li>
<li>Inicializar la BD, usando el nuevo script postgresql-setup:<a name='more'></a><br />
<code><br />
# postgresql-setup initdb<br />
</code><br />
</li>
<li>Iniciar el daemon:<br />
<code><br />
# service postgresql start<br />
</code><br />
</li>
<li>Arrancar el daemon por defecto en los niveles de inicio 2, 3 y 5:<br />
<code><br />
# chkconfig --levels 235 postgresql on<br />
</code><br />
</li>
</ul>Luego deberemos realizar la configuración básica del mismo modo que en versiones anteriores de Fedora y que describo en <a href="http://bitexperience.blogspot.com/2011/08/configuracion-basica-de-postgresql.html">este post</a>.<br />
<br />
Eso es todo, ahora podremos trabajar sin problemas.<br />
<br />Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-84377568484908288622012-06-29T16:33:00.002-05:002012-06-30T17:24:31.696-05:00Cambiar directorio de letras de canciones en rhythmbox en Fedora 17<div style="text-align: justify;">En versiones anteriores, el plugin de lyrics de rhythmbox permitía configurar el directorio en el que se almacenaban los archivos de letras, pero en las últimas versiones (que he probado con Fedora 16 y Fedora 17) esta opción no está activa, lo cual puede ser molesto si se tiene un directorio en el que hemos almacenado las letras de las canciones que nos gustan.<br />
<br />
Ahora rhythmbox define automáticamente el directorio dentro del directorio cache del usuario (/home/usuario/.cache/rhythmbox/lyrics), lo cual es incluso más molesto si queremos usar los mismos archivos ya descargados para varios usuarios.<br />
<br />
La solución es bastante sencilla:<a name='more'></a> reemplazamos o creamos un enlace en el directorio de caché de rhythmbox que enlace a nuestro directorio de letras de canciones. Suponiendo que nuestro archivo de letras se encuentra en /multimedia/musica/letras, lo que haremos será lo siguiente:<br />
<code><br />
[usuario@localhost ~]$ ln -s /multimedia/musica/letras/ /home/usuario/.cache/rhythmbox/lyrics<br />
</code><br />
De este modo podremos acceder a los archivos que ya hayamos descargado y además se irań guardando los nuevos que descargue rhythmbox.<br />
<br />
Hay que tener en cuenta que los directorios y archivos dentro de nuestro directorio de letras deberán estar nombrados según el estándar de rhythmbox, el cual define un directorio por cada artista y el nombre del archivo será el nombre de la canción con extensión .lyric. Los nombres son todos en minúsculas, según el ID tag definido para cada canción.<br />
</div>Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-84851372445948693572012-06-23T17:27:00.003-05:002012-06-30T17:24:39.444-05:00Agregar resolución de monitor en Fedora 17<div style="text-align: justify;">Bueno, luego de actualizar a Fedora 16 hace poco, decidí volver a actualizar a Fedora 17 porque, la verdad sea dicha, no me gustó mucho Verne.<br />
<br />
Como de costumbre, lo primero que tengo que hacer es configurar mi monitor LG para que reconozca su resolución de 1360x768. Pero esta vez decidí no hacerlo según mi método anterior usando los drivers propietarios de Nvidia, dado que no me funcionaron muy bien con Verne.<br />
<br />
En realidad el proceso es bastante sencillo, una vez se sabe cómo hacerlo :)<br />
<a name='more'></a><br />
Lo primero es obtener el nombre del monitor:<br />
<code><br />
[root@localhost ~]# xrandr <br />
Screen 0: minimum 320 x 200, current 1024 x 768, maximum 4096 x 4096<br />
VGA-1 connected 1024x768+0+0 (normal left inverted right x axis y axis) 0mm x 0mm<br />
1024x768 60.0*+ <br />
800x600 60.3 56.2 <br />
848x480 60.0 <br />
640x480 59.9 <br />
</code><br />
En el resultado del comando vemos que el nombre del monitor por defecto es VGA-1.<br />
<br />
Ahora necesitamos los parámetros de Modeline para la resolución que queremos agregar, en mi caso 1360x768:<br />
<code><br />
[root@localhost ~]# cvt 1360 768<br />
# 1360x768 59.80 Hz (CVT) hsync: 47.72 kHz; pclk: 84.75 MHz<br />
Modeline "1360x768_60.00" 84.75 1360 1432 1568 1776 768 771 781 798 -hsync +vsync<br />
</code><br />
<br />
A continuación creamos el archivo de configuración para el monitor, el directorio es /etc/X11/xorg.conf.d/ el nombre no es realmente importante, en mi caso lo llamé 00-monitor.conf. El archivo lo creamos como root y su contenido será el siguiente:<br />
<code><br />
Section "Monitor"<br />
Identifier "VGA-1"<br />
Modeline "1360x768_60.00" 84.75 1360 1432 1568 1776 768 771 781 798 -hsync +vsync<br />
Option "PreferredMode" "1360x768_60.00"<br />
EndSection<br />
</code><br />
La línea Modeline es la que obtuvimos con el comando cvt, y la opción PreferredMode nos permite usar la nueva resolución por defecto.<br />
<br />
Para finalizar reiniciamos y listo!<br />
<br />
Espero sirva.<br />
<br />
Fuentes: <a href="http://ask.fedoraproject.org/question/951/how-do-i-change-my-monitors-resolution" target="_blank">Ask Fedora</a> y <a href="https://wiki.archlinux.org/index.php/Xrandr#Scripts" target="_blank">ArchWiki</a><br />
<br />
</div><br />
<br />Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-31075752490808854812012-03-26T17:17:00.000-05:002012-12-10T09:33:49.049-05:00Obtener el último registro insertado en una tabla en PostgreSQL<div style="text-align: justify;">Podría parecer algo muy sencillo pero la verdad es que a veces puede no serlo tanto, sobretodo si no tuvimos en cuenta esto en el diseño de la BD. Según la tabla podemos encontrarnos con 2 posibilidades:<br />
<br />
<ul><li>Tablas sin campo único autoincrementable</li>
<li>Tablas con campo único autoincrementable</li>
</ul><br />
Veamos cada una en detalle:<br />
<a name='more'></a><br />
<br />
<h4>Tabla sin campo único autoincrementable</h4><br />
Si no incluimos un campo autoincrementable en la tabla, puede resultar muy tedioso ya que PostgreSQL reordena los datos cada que hay una actualización de un registro, por lo que no siempre obtendremos el listado en el mismo orden, pero dependiendo de la situación tenemos varias opciones:<br />
<ul><li>Usar OIDs: Por defecto PostgreSQL agrega este campo 'oculto' a cada registro pero en la misma documentación vemos que no son fiables como identificador, además habría que habilitarlos al crear la tabla ya que desde la versión 8.1 los OIDs están deshabilitados por defecto.<br />
<code>SELECT * FROM tabla ORDER BY oid DESC LIMIT 1;</code><br />
</li>
<li>Consulta inmediatamente después de insertar el registro: Apenas insertamos el registro podemos obtener el último registro con la siguiente consulta:<br />
<code>SELECT * FROM tabla LIMIT 1;</code><br />
Pero no funcionará después de que se hagan otro tipo de actualizaciones sobre la tabla, así que tampoco sirve mucho.<br />
</li>
<li>Usar claúsula <a href="http://www.postgresql.org/docs/current/interactive/sql-insert.html" target="_blank">RETURNING</a> en el INSERT: Igual que el anterior sólo sirve inmediatamente después de la inserción del registro.<br />
</li>
</ul><br />
<h4>Tabla con campo único autoincrementable</h4><br />
En este caso todo es mucho más sencillo. Suponiendo que el campo se llama id tenemos varias opciones según lo que querramos:<br />
<ul><li>Obtener el último ID insertado mediante consulta: Si queremos conocer el último id insertado en la tabla usaremos la función agregada MAX:<br />
<code>SELECT MAX(id) FROM tabla;</code><br />
</li>
<li>Obtener el último registro insertado: Si lo que queremos es la tupla completa, con varios o todos sus campos las consultas que usaremos son estas:<br />
<code>SELECT * FROM tabla ORDER BY id DESC LIMIT 1;<br />
SELECT id, campo1, campo2 FROM tabla ORDER BY id DESC LIMIT 1;</code><br />
Esta consulta simplemente ordena por la llave primaria autoincrementable en orden descendente y entrega sólo un resultado.</li>
<li>Obtener el último ID insertado en una sesión de trabajo: Al estar vinculado con una secuencia cada campo autoincrementable (tipo SERIAL) podemos usar las <a href="http://www.postgresql.org/docs/9.1/static/functions-sequence.html" target="_blank">funciones de manipulación de secuencias</a>. De estas podemos usar la función lastval() que nos entregará el valor más reciente de cualquier secuencia en la sesión actual. De este modo podemos, por ejemplo, asignar su valor a una variable si estamos en una función:<br />
<code>ultimo_id := lastval();</code><br />
</li>
</ul><br />
Por esta y otras razones es una buena práctica usar llaves primarias autoincrementables en las tablas.</div>Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-39389913815737068192011-12-28T09:13:00.000-05:002012-04-14T15:37:54.583-05:005 tipos de usuarios de linux muy molestos ¿cuál eres tú?Hoy he visto un <a href="http://paraisolinux.com/5-tipos-de-usuarios-de-linux-muy-molestos/">post</a> entretenido que no deja de ser muy cierto, que muestra algunos tipos de usuarios de linux que resultan ser muy molestos.<br />
<br />
¿Cuál será el más molesto? Yo debo decir que de casi todos tengo o he tenido al menos un poco :D<br />
<br />
<a href="http://paraisolinux.com/5-tipos-de-usuarios-de-linux-muy-molestos/" target="_blank">Leánlo acá</a>Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-3637081718121805262011-12-08T16:30:00.001-05:002012-12-10T09:33:49.053-05:00Construir SQL dinámicos en PostgreSQL con PL/pgSQLA veces queremos ejecutar un SQL dentro de una función (como en un trigger) en el que los nombres de los campos o de la tabla a consultar varian, es decir que es dinámico.<br />
<br />
Si estamos usando el lenguaje procedimental de PostgreSQL (PL/pgSQL), esto puede hacerse a través de concatenación de cadenas de texto.<br />
<br />
Para el ejemplo, supongamos que queremos una función que nos genere el código siguiente para un campo y tenemos la misma estructura para diferentes tablas, así que queremos que la función sea reutilizable. El código es muy sencillo: tomar el último valor para el campo, sumarle 1 y devolver el resultado:<br />
<code></code><br />
<a name='more'></a><code>
CREATE OR REPLACE FUNCTION generar_siguiente_codigo(tabla_nombre varchar, col1_nombre varchar, col2_nombre varchar, col1_valor int) RETURNS smallint AS $$<br />
DECLARE<br />
max_cod SMALLINT;<br />
sig_cod SMALLINT;<br />
BEGIN<br />
<br />
EXECUTE 'SELECT MAX('|| col2_nombre ||') FROM '|| tabla_nombre::regclass ||' WHERE '|| col1_nombre ||' = $1'<br />
INTO max_cod<br />
USING col1_valor;<br />
<br />
IF max_cod IS NULL THEN<br />
max_cod := 0;<br />
END IF;<br />
<br />
sig_cod := max_cod + 1;<br />
<br />
RETURN sig_cod;<br />
END<br />
$$<br />
LANGUAGE 'plpgsql';<br />
</code><br />
El comando EXECUTE es el que nos permite ejecutar el SQL creado, que es sólo una concatenación de textos mediante el operador ||. Es necesario usar el identificador regclass para el nombre de las tablas o el SQL no funcionará.<br />
<br />
Y eso es todo.Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-35633013807024763742011-12-06T09:20:00.001-05:002012-12-10T09:33:49.051-05:00Uso de LIKE con campos de tipo numérico en PostgreSQLEl operador LIKE de SQL sirve para comparar expresiones según un patrón (más info <a href="http://www.postgresql.org/docs/8.4/static/functions-matching.html">acá</a>). Pero funciona sólo con campos tipo texto (CHAR, VARCHAR, TEXT). Si se quiere hacer una comparación sobre un campo tipo INTEGER no va a funcionar (tuve que hacer dicha comparación al configurar un campo de formulario con autocompletado con Symfony).<br />
<br />
Sin embargo se puede hacer y el truco es muy sencillo, ya que simplemente debemos 'convertir' el campo de tipo entero a una cadena de texto. Esto se hace con la función <a href="http://www.postgresql.org/docs/current/static/functions-formatting.html">TO_CHAR</a>. Veamos un ejemplo de consulta sencilla con esta función:<br />
<code></code><br />
<a name='more'></a><code>
SELECT codigo FROM tabla WHERE TO_CHAR(codigo, '9999') LIKE '%texto%';<br />
</code><br />
Acá le estoy dando formato de 4 dígitos al código pero si hay códigos más largos habrá que usar más dígitos en el formato de TO_CHAR:<br />
<code><br />
SELECT codigo FROM tabla WHERE TO_CHAR(codigo, '9999999999') LIKE '%texto%';<br />
</code><br />
La función entregará espacios vacíos para los números que tengan menos dígitos. Si queremos eliminarlos podemos hacerlo con la función <a href="http://www.postgresql.org/docs/8.4/static/functions-string.html">TRIM</a>:<br />
<code><br />
SELECT codigo FROM tabla WHERE TRIM(TO_CHAR(codigo, '9999999999')) LIKE '%texto%';<br />
</code><br />
Como dato curioso de esta última consulta debo decir que al usar esta consulta con el widget de autocompletado de sfFormExtraPlugin no funciona correctamente, ya que no entrega todos los resultados, así que he tenido que dejar la forma más sencilla de la consulta.Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-81908954328975446512011-10-11T15:37:00.000-05:002011-12-10T18:24:46.039-05:00Comando para eliminar una línea de un archivo de texto según expresión regularSi queremos eliminar una sola línea de texto de un archivo de texto, podemos usar el comando sed de la siguiente forma:<br />
<code><br />
$ sed -i '/regexp/d' /ruta_del_archivo<br />
</code><br />
La opción -i le indica a sed que edite y guarde los cambios hechos al archivo. Veamos un ejemplo práctico:<br />
<br />
<a name='more'></a>En mi caso he usado este comando para eliminar las etiquetas @version de algunos archivos autogenerados por symfony (como en los módulos creados con el admin generator), para hacer esto lo combino con find para buscar recursivamente los archivos donde se encuentre esta etiqueta y los paso como entrada para el comando sed que ya vimos:<br />
<code><br />
$ find modules/ -name *.php -exec grep -l '@version' {} \; | xargs sed -i '/^ \* @version/d'<br />
</code><br />
En este comando le indico a sed que elimine todas las líneas que comiencen con el texto ' * @version'. Usando el hecho de que estas líneas son las únicas que terminan con el caracter '$' podemos cambiar la expresión regular anterior así:<br />
<code><br />
$ find modules/ -name *.php -exec grep -l '@version' {} \; | xargs sed -i '/\$$/d'<br />
</code><br />
Aunque yo prefiero la primera opción ya que es más específica.<br />
<br />
Fuentes: <a href="http://www.gentoo.org/doc/en/articles/l-sed1.xml">gentoo.org</a>, <a href="http://www.zytrax.com/tech/web/regex.htm">zytrax.com</a>Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-6903368717975054212011-09-24T15:34:00.001-05:002011-12-10T18:35:40.139-05:00Comandos para obtener información del sistema en Linux<div style="text-align: justify;">Esta es una compilación de algunos comandos muy básicos que uso habitualmente para obtener información sobre el sistema en el que esté trabajando:<br />
<ul><li>Información del sistema operativo:<br />
<code><br />
$ uname -a<br />
</code><br />
</li>
<li>Información de particiones:<br />
<code><br />
# fdisk -l<br />
o<br />
$ df -h<br />
</code><br />
</li>
<a name='more'></a>
<li>Información de la CPU (se puede usar cualquier visor de texto):<br />
<code><br />
$ cat /proc/cpuinfo<br />
</code><br />
</li>
<li>Información de uso de memoria:<br />
<code><br />
$ cat /proc/meminfo<br />
</code><br />
</li>
<li>Ver mensajes del arranque:<br />
<code><br />
$ dmesg<br />
</code><br />
</li>
<li>Información de dispositivos de hardware:<br />
<code><br />
$ lspci<br />
o<br />
$ dmidecode<br />
</code><br />
Para dmidecode se puede especificar el tipo de información que queremos ver. Si queremos por ejemplo ver información de la board:<br />
<code><br />
$ dmidecode --type baseboard<br />
</code><br />
Ver manual de dmidecode para más tipos (ej: memory, processor).<br />
</li>
</ul>Hay más pero estos son muy buenos y funcionan en todas o por lo menos en la mayoría de distros linux.</div>Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-87717210676097375192011-09-24T15:21:00.000-05:002011-12-10T18:28:00.562-05:00Campos únicos de tipo entero en Doctrine 1.2<div style="text-align: justify;">He comprobado personalmente en un par de proyectos que curiosamente Doctrine no genera las restricciones UNIQUE sobre campos de tipo entero (int) en la base de datos, aunque sí lo hace con campos tipo texto. Por ejemplo, si tenemos una clase definida así:<br />
<code><br />
Mascota:</code><br />
<code> connection: doctrine<br />
tableName: mascota<br />
actAs: [Timestampable]<br />
columns:<br />
id:<br />
type: integer(4)<br />
primary: true<br />
autoincrement: true<br />
dueno_id:<br />
type: integer(4)<br />
unique: true<br />
descripcion:<br />
type: string()<br />
notnull: true<br />
</code><br />
<a name='more'></a><br />
Symfony ejecutará este SQL en el gestor de bases de datos:<br />
<code><br />
CREATE TABLE mascota (id SERIAL, dueno_id INT, descripcion TEXT NOT NULL, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL, PRIMARY KEY(id));<br />
</code><br />
Con lo que no tendremos la validación en la base de datos (podemos comprobarlo al ver la descripción de la tabla en el gestor), aunque por lo menos sí lo hace en la aplicación, ya que si miramos el formulario base veremos el postvalidator con la restricción unique sobre el campo 'dueno_id':<br />
<code><br />
...<br />
$this->validatorSchema->setPostValidator(<br />
new sfValidatorDoctrineUnique(array('model' => 'AbFacturacionDesvinculacion', 'column' => array('suscripcion_id')))<br />
);<br />
...<br />
</code><br />
Si cambiamos el tipo del campo 'dueno_id' a string(255) por ejemplo, symfony ejecutará este sql:<br />
<code><br />
CREATE TABLE mascota (id SERIAL, dueno_id VARCHAR(255) UNIQUE, descripcion TEXT NOT NULL, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL, PRIMARY KEY(id));<br />
</code><br />
Pero evidentemente eso no es lo que queremos y como soy paranoico yo quiero mi campo tipo entero con restricción unique definido en la base de datos. Para lograrlo tendremos que definirlo como un índice único de la clase:<br />
<code><br />
Mascota:<br />
connection: doctrine<br />
tableName: mascota<br />
actAs: [Timestampable]<br />
columns:<br />
id:<br />
type: integer(4)<br />
primary: true</code><code> </code><code> autoincrement: true</code><code> </code><code>dueno_id:<br />
</code><code> </code><code>type: integer(4)</code><code> </code><code>descripcion:</code><code> </code><code> type: string()</code><code> </code><code> notnull: true<br />
indexes:<br />
mascota_dueno_index:<br />
fields: [dueno_id]<br />
type: unique<br />
</code><br />
De este modo la tabla creada en la base de datos corresponderá a este sql:<br />
<code><br />
CREATE TABLE mascota (id SERIAL, dueno_id INT UNIQUE, descripcion TEXT NOT NULL, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL, PRIMARY KEY(id));<br />
</code><br />
Curioso, aunque un poco molesto la verdad ya que es un comportamiento algo errático poco deseable.</div>Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-34081715875726749102011-09-08T16:07:00.001-05:002012-09-21T18:14:58.549-05:00Comando para filtrar el historial de la consola<div style="text-align: justify;">El comando history es un comando muy sencillo pero que resulta muy útil, ya que nos muestra todo el historial de líneas de comando y nos puede ahorrar unos cuantos tecleos, pero a veces podemos gastar más tiempo buscando esa línea de comando que el que demoraríamos escribiéndola. Pues yo uso esta forma muy sencilla de filtrar los resultados de history. Podemos escribir parte del comando que buscamos, si por ejemplo buscamos un comando symfony: <br />
<code> $ history | grep 'symfony' </code> <br />
O si buscamos un comando que hayamos usado de svn: <br />
<code> $ history | grep 'svn </code> <br />
</div>Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-83955535172774081982011-09-08T15:30:00.000-05:002012-12-10T09:33:49.055-05:00División entre enteros en PL/pgSQL<div style="text-align: justify;">La división entre enteros elimina automáticamente la parte decimal del resultado. Esto puede ser contraproducente si necesitamos cálculos que hagan uso de la parte decimal, incluso si simplemente queremos redondear y obtener un entero, ya que obtendremos un resultado erróneo cuando la parte decimal sea mayor a 0.5. Consideremos por ejemplo que vamos a almacenar el valor del salario mensual de un contrato y queremos calcular el valor del día como valor entero y guardarlo para posteriores operaciones:<br />
<br />
<code>DECLARE<br />
...<br />
BEGIN<br />
...<br />
NEW.salario_base_diario := NEW.salario_base_mensual / 30;<br />
</code><br />
<a name='more'></a><br />
Si el salario_base_mensual es de 348, dividiendo en 30 obtendremos 11.6, al ser salario_base_diario entero esperaríamos tener un valor de 12, pero no. Postgres simplemente desprecia la parte decimal con lo que el valor que obtendremos será 11, lo cual puede generar muy malos resultados después. Para obtener un resultado adecuado es mejor almacenar una o varias de las variables usadas como tipo NUMERIC aunque sean enteros, de este modo el resultado será NUMERIC y podremos hacer el redondeo adecuado, así:<br />
<br />
<code> DECLARE<br />
...<br />
var_nuevo_salario_base_mensual NUMERIC;<br />
BEGIN<br />
...<br />
-- Redondear el salario mensual a dos dígitos decimales<br />
var_nuevo_salario_base_mensual := round(NEW.salario_base_mensual, 2);<br />
NEW.salario_base_diario := round(var_nuevo_salario_base_mensual / cfg_dias_mes);</code><br />
<br />
Con este pequeño cambio obtendremos lo que queremos, de modo que: 348/30 = 11.60. Y este resultado será redondeado correctamente a 12. </div>Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-35222151633060041952011-08-13T16:30:00.000-05:002011-12-10T18:36:41.641-05:00Configuración básica de PostgreSQL<div style="text-align: justify;">Luego de tener PostgreSQL instalado hay que realizar una configuración básica (o más bien mínima) a nuestro entorno de trabajo, en particular si vamos a usar ciertas herramientas (como PgAdmin o Symfony) que deben poder conectarse al servidor postgres. Los pasos serían los siguientes:<br />
<ul><li>Cambiar password al usuario de sistema postgres (no es estrictamente necesario, pero yo prefiero hacerlo):<br />
<code><br />
$ su -<br />
# passwd postgres<br />
</code><br />
</li>
<li>Cambiar password al usuario admin dentro de postgres en la bd template1:<br />
<code><br />
$ su postgres<br />
$ psql template1<br />
template1=# ALTER USER postgres WITH PASSWORD 'nuevo_password';<br />
</code><br />
Este método no me gusta mucho porque se puede ver la contraseña en el historial. Yo prefiero hacerlo así:<br />
<a name='more'></a><br />
<code><br />
template1=# \password postgres<br />
</code><br />
Con este método se nos pedirá la nueva contraseña y la confirmación igual que el comando passwd.<br />
</li>
<li>Cambiar el método de autenticación en el archivo pg_hba.conf (en fedora en el directorio /var/lib/pgsql/data/), para usar MD5, ya que IDENT da conflictos:<br />
<code><br />
# "local" is for Unix domain socket connections only<br />
local all all md5<br />
# IPv4 local connections:<br />
host all all 127.0.0.1/32 md5<br />
# IPv6 local connections:<br />
host all all ::1/128 md5<br />
</code><br />
Doctrine usa una conexión IPV6, así que hay que cambiar el método de autenticación para estas conexiones.<br />
</li>
</ul>Ahora no deberíamos tener problemas para conectarnos al servidor postgres desde PgAdmin o Symfony (con Doctrine o Propel) y por supuesto desde la consola con el comando psql -U postgres.</div>Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-13496728456569357422011-08-08T14:53:00.000-05:002011-08-08T14:53:11.782-05:00Problema de arranque de PostgreSql en Fedora 12 y Fedora 14<div style="text-align: justify;">Luego de instalar nuestro sistema Fedora y PostgreSQL, veremos que este último no puede arrancarse ni configurarse para arrancar con el sistema usando system-config-services. Por alguna razón el clúster de postgresql no es inicializado al instalarlo y esto no lo dice el error que nos genera al tratar de iniciar el servicio por consola:<br />
<code><br />
# /etc/init.d/postgresql start<br />
</code><br />
El error nos dice que ejecutemos el siguiente comando para inicializar el cluster:<br />
<code><br />
# service postgresql initdb<br />
</code><br />
Ahora sí podremos iniciar el daemon por consola o con system-config-services.<br />
</div>Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-90819053013905646042011-07-23T13:10:00.000-05:002011-12-10T18:34:07.640-05:00Warning: require(): Unable to allocate memory for poolTrabajando en unos proyectos con symfony simultáneamente me encontré con este error en algunos pero no en todos ellos:<br />
Warning: require(): Unable to allocate memory for pool<br />
<br />
La solución fue sencilla, simplemente limpiar la caché de symfony:<br />
<code><br />
symfony cc<br />
</code><br />
Pero obviamente lo importante es saber porqué ocurrió tal error. Luego de buscar un poco dí con este <a href="http://pecl.php.net/bugs/bug.php?id=16966">enlace</a> en el que indican que al parecer se debe a un bug de algunas combinaciones de versiones de APC y PHP (yo tengo APC 3.1.9 y PHP 3.5.6), pero no hay solución definitiva. Será estar pendiente y estar limpiando la caché de vez en cuando.Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.comtag:blogger.com,1999:blog-8040637117485206649.post-44766389630347617232011-07-19T21:09:00.000-05:002011-12-10T18:37:27.965-05:00Buscar y reemplazar un texto de forma recursiva<div style="text-align: justify;">Las maravillosas tuberías (pipes) en linux nos permiten hacer maravillas como estas en la consola. Acá dejo varios casos de buscar y reemplazar:<br />
<br />
<ul><li>Buscar y Reemplazar un texto recursivamente:<br />
<code><br />
$ find . -name "*.php" -print | xargs sed -i 's/texto_a_buscar/texto_nuevo/g'<br />
</code><br />
</li>
<li>Igual que el anterior pero para poder buscar saltos de línea:<br />
<a name='more'></a><br />
<code><br />
$ find . -name *.php -print | xargs sed -i ':a;N;$!ba;s/texto_a\nbuscar/texto_a\nreemplazar/g'<br />
</code><br />
Puesto que se trata de una expresión regular habrá que escapar los caracteres especiales, tales como '\' y '.' con '\'.<br />
</li>
<li>Encontrar archivos donde ocurre el 'texto', de forma recursiva y reemplazar un texto:<br />
<code><br />
$ find . -exec grep -li 'texto' {} \; | xargs sed -i 's/texto_a_buscar/texto_nuevo/g'<br />
</code><br />
Usar este comando así en un repositorio de svn es mala idea ya que también editará los archivos en los directorios ocultos de svn. Para evitar esto es mejor filtrar por tipo de archivo si es posible:<br />
<code><br />
$ find . -name *.php -exec grep -li 'texto' {} \; | xargs sed -i 's/texto_a_buscar/texto_nuevo/g'<br />
</code><br />
</li>
</ul></div>Fabian Barrerahttp://www.blogger.com/profile/08695090471952487694noreply@blogger.com