Архива за 'Programming' категорију

MySQL - NULL polja i sortiranje

October 4, 2009

Vrlo često imamo situaciju da želimo sortiranje po nekom određenom polju u tebeli ali tako da se NULL polja nikada ne pojavljuju na početku. Tipičan primer je recimo frontend koji prikazuje podatke iz neke tabele, gde se klikom na header kolone obavlja sortiranje po rastućem (asc) ili opadajućem (desc) poretku.

Obzirom da se ja u poslednje vreme dosta bavim domenima, kreirao sam jednu tabelu sa par svojih domena, čisto kao demonstraciju koncepta.

mysql> select * from domains; +----+-------------------+-------------+ | id | domain | expire_date | +----+-------------------+-------------+ | 1 | dinke.net | 2010-01-17 | | 2 | lampix.net | 2009-12-26 | | 3 | blogodak.com | 2010-09-08 | | 4 | maestrodesert.com | 2009-09-11 | | 5 | nepostojeci.com | NULL | +----+-------------------+-------------+ 5 rows in set (0.00 sec)

Dakle problem, želim sortiranje po expire_date polju ali tako da se NULL polje (recimo domen koji još nije regovan ili je istekao) uvek pojavljuje na kraju. Po defaultu NULL se javlja na početku ako sortiramo u rastućem (ASC) orderu odnosno na kraju ako sortiramo po opadajućem (desc) orderu.

mysql> select * from domains order by expire_date asc; +----+-------------------+-------------+ | id | domain | expire_date | +----+-------------------+-------------+ | 5 | nepostojeci.com | NULL | | 4 | maestrodesert.com | 2009-09-11 | | 2 | lampix.net | 2009-12-26 | | 1 | dinke.net | 2010-01-17 | | 3 | blogodak.com | 2010-09-08 | +----+-------------------+-------------+ 5 rows in set (0.00 sec) mysql> select * from domains order by expire_date desc; +----+-------------------+-------------+ | id | domain | expire_date | +----+-------------------+-------------+ | 3 | blogodak.com | 2010-09-08 | | 1 | dinke.net | 2010-01-17 | | 2 | lampix.net | 2009-12-26 | | 4 | maestrodesert.com | 2009-09-11 | | 5 | nepostojeci.com | NULL | +----+-------------------+-------------+ 5 rows in set (0.00 sec)

Problem sortiranja ćemo rešiti korišćenjem MySQL-ove IF f-je, a rešenje je:

mysql> select * from domains order by if(expire_date is null, 1, 0), expire_date asc; +----+-------------------+-------------+ | id | domain | expire_date | +----+-------------------+-------------+ | 4 | maestrodesert.com | 2009-09-11 | | 2 | lampix.net | 2009-12-26 | | 1 | dinke.net | 2010-01-17 | | 3 | blogodak.com | 2010-09-08 | | 5 | nepostojeci.com | NULL | +----+-------------------+-------------+ 5 rows in set (0.00 sec)

MySQL-ova IF f-ja slična je ternarnom operatoru, tj. vraća prvi argument ako je iskaz tačan odnosno drugi u slučaju da nije, dakle u ovom slučaju vraća 1 za null vrednosti odnosno 0 za ostale, čime dobijamo upravo prikaz koji želimo tj. NULL polje na kraju liste.

Znam da ovo može delovati pomalo konfuzno pa ću otići još jedan korak dalje i dodati još jedno polje u našoj tabeli čisto radi razjašnjenja šta se ovde tačno događa:

mysql> alter table domains add column nullorder tinyint not null; Query OK, 5 rows affected (0.01 sec) Records: 5 Duplicates: 0 Warnings: 0

a zatim i update-ovati vrednosti nullorder polja tako da sadrže vrednost IF iskaza odozgo:

mysql> update domains set nullorder = if(expire_date is null, 1, 0); Query OK, 1 row affected (0.00 sec) Rows matched: 5 Changed: 1 Warnings: 0 mysql> select * from domains; +----+-------------------+-------------+-----------+ | id | domain | expire_date | nullorder | +----+-------------------+-------------+-----------+ | 1 | dinke.net | 2010-01-17 | 0 | | 2 | lampix.net | 2009-12-26 | 0 | | 3 | blogodak.com | 2010-09-08 | 0 | | 4 | maestrodesert.com | 2009-09-11 | 0 | | 5 | nepostojeci.com | NULL | 1 | +----+-------------------+-------------+-----------+ 5 rows in set (0.00 sec)

Sve u svemu naš gornji query iz rešenja problema:

select * from domains order by if(expire_date is null, 1, 0), expire_date asc;

Potpuno je isto što i ovaj query:

mysql> select * from domains order by nullorder, expire_date; +----+-------------------+-------------+-----------+ | id | domain | expire_date | nullorder | +----+-------------------+-------------+-----------+ | 4 | maestrodesert.com | 2009-09-11 | 0 | | 2 | lampix.net | 2009-12-26 | 0 | | 1 | dinke.net | 2010-01-17 | 0 | | 3 | blogodak.com | 2010-09-08 | 0 | | 5 | nepostojeci.com | NULL | 1 | +----+-------------------+-------------+-----------+ 5 rows in set (0.00 sec)

osim što naravno nullorder polje nismo morali da kreiramo.

Naravno na sličan način možemo dobiti NULL polja na početku u desc prikazu (za slučaj da je to ikome potrebno).

Browser class by Bluesman

August 31, 2009

Moj prijatelj i kolega Goran Pilipović (u zajednici poznatiji kao Bluesman) nedavno mi je poslao svoju verziju moje Browser Detection klase.

Praktično radi se o potpuno novom kodu jer za razliku od moje poprilično jednostave klase Blues koristi značajno veći broj metoda. Evo primera korišćenja:

require_once "class.Browser.php"; Browser::get(); echo "<pre>"; echo "<br />User Agent: ".Browser::ua(); echo "<br />Browser Id: ".Browser::id(); echo "<br />Browser Name: ".Browser::name(); echo "<br />Browser Version: ".Browser::version(); echo "<br />OS: ".Browser::os(); echo "<br />Device: ".Browser::device(); echo "<br />Platform: ".Browser::platform(); echo "<br />Is PC: ".yesno(Browser::isPc()); echo "<br />Is Windows: ".yesno(Browser::isWindows()); echo "<br />Is Mac: ".yesno(Browser::isMac()); echo "<br />Is Linux: ".yesno(Browser::isLinux()); echo "<br />Is Symbian: ".yesno(Browser::isSymbian()); echo "<br />Is IE: ".yesno(Browser::isIe()); echo "<br />Is Safari: ".yesno(Browser::isSafari()); echo "<br />Is Firefox: ".yesno(Browser::isFirefox()); echo "<br />Is Chrome: ".yesno(Browser::isChrome()); echo "<br />Is Opera: ".yesno(Browser::isOpera()); echo "<br />Is iPhone: ".yesno(Browser::isIphone()); echo "<br />Is Handlheld: ".yesno(Browser::isHandheld()); echo "<br />Is Phone: ".yesno(Browser::isPhone()); echo "<br />Is Console: ".yesno(Browser::isConsole()); echo "<br />Is Terminal: ".yesno(Browser::isTerminal()); echo "</pre>";

Kompletnu klasu kao i test fajl sa primerima možete skinuti ovde. U slučaju da pronađete neki bug možete ga prijaviti ovde (samo ostavite komentar sa opisom problema).

PHP 5.2.10 - up, up and… down…

June 20, 2009

PHP dev team released new version of PHP 5.2.x two days ago. This version brings a few security fixes and a lot of bug squashing. Alas, there was one seriously wrong fix. A bug that we’ll call "PHP safe_mode bypass with exec/system/passthru" was supposedly fixed in this latest version. Supposedly. It’s still there. And here is a proof of concept. I’ll wait with upgrading to 5.2.10.

Koji programski jezik učiti

April 14, 2009

Pitanje iz naslova jedno je od najčešće postavljenih pitanja svršenih diplomaca ili ljudi koji ulaze u svet programiranja. Pitanje nije trivijalno iz jednostavog razloga što dobro odabrana tehnologija znači veće mogućnosti za pronalaženje posla, bolje plaćen posao, … u najkraćem - svetliju budućnost.

Sve do juče na pitanje iz naslova bez mnogo razmišljanja odgovarao bih - PHP. Jednostavno, PHP poslova uvek ima, PHP se relativno brzo nauči, tako da za relativno kratko vreme možete doći do posla. Čak i za one bez ikakvog iskustva, otvorenost Web platforme omogućava početnicima da naprave sopstveni sajt, sajt komšijine prodavnice, prijatelja … whatever … i tako za kratko vreme steknu kakve takve reference i iskustvo - stvar koju tako mnogo tražimo mi koji tragamo za kvalitetnim developerima.

Elem, pre nekoliko dana kolega iz FBM-a zamolio me je da otvorim novu poziciju za iPhone developere, nakon čega sam nažalost jako brzo saznao da se broj iPhone Developera u Srbiji može nabrojati “na prste” jedne ruke.

Međutim situacija u svetu a naročito u USA je dramatično drugačija. Broj downloada iPhone aplikacija na Apple Application Store-u bliži se cifri sa 9 nula, a trenutno na listi najprodavanijih knjiga na Amazonu u kategoriji Računari i Internet možete naći samo nekoliko programerskih knjiga, od kojih su apsolutno sve vezane za iPhone. Ista priča je i kada odete i na kategoriju Programming gde su tri od prvih pet naslova knjige o razvoju iPhone aplikacija!

iPhone Application Develpment Books

Nažalost cene Apple računara i nedostatak zvanične iPhone podrške u Srbiji i dalje je veliki problem u ovoj priči. No, za one za koje taj problem nije nepremostiv, današnji odgovor na pitanje iz naslova definitivno bi bio Objective C. Nisam siguran da ću čak i ja odoleti iskušenju …

WRITE IN C

February 27, 2009
WRITE IN C (sung to The Beatles “Let it Be”) When I find my code in tons of trouble, Friends and colleagues come to me, Speaking words of wisdom: “Write in C.” As the deadline fast approaches, And bugs are all that I can see, Somewhere, someone whispers” “Write in C.” Write in C, write in C, Write in C, write in [...]

Browser Detection Update

November 25, 2008

Danas sam sticajem okolnosti bio u prilici da nakon duzeg vremena updatujem svoju klasu za detekciju browsera. Radi se o klasi koja omogucava detektovanje tacne verzije Browsera/OS-a na osnovu user agenta browsera.

Npr: Mozilla Firefox 3.0.4 / Mac OS X

Može biti od koristi u slučaju da želite da korisnike određenih browsera redirektujete na posebne strane, kod pravljenja sopstvene statistike korišćenosti browsera itd.

Update sadrži dodatu podršku za Google Chrome, iPhone i Windows Vista OS.

Kompletan kod sa primerima možete downloadovati ovde

Izlaz scripta na ekranu i fajlu

November 18, 2008

Zbog prirode svog posla gotovo svakodnevno se bakćem sa raznoraznim php cli scriptovima. Često su u pitanju programi koji rade jako dugo (satima, neki čak i danima), ponekad praveći veliku količinu outputa, te zbog toga praktikujem da iste startujem unutar screen sesije, kako bih uvek mogao da se ponovo ulogujem u sesiju i bacim pogled kako stoje stvari.

Veoma je dobro logovati kompletan output skripta u fajl, za slučaj da je nešto pošlo naopako, ali nekako sam uvek voleo da onako “live” vidim na ekranu šta se dešava, umesto da mi ceo izlaz stoji u nekom tamo log fajlu. No, zahvaljući unix tee komandi i ovom postu, danas sam naučio da mogu imati obadva.

./script.php | tee out.log

Ovo će kreirati fajl out.log i usmeriti kompletan output scripta u njega, ali će isti biti prikazan i na ekranu (STDOUT). U slučaju da želite samo da dodate (append) output u postojeći log, nema problema:

./script.php | tee -a out.log

Perl za kišni dan…

September 14, 2008

Divan kišni dan. Ne znam za vas ali ja uživam kada pada kiša. Danas sam se nešto igrao u Perlu pa rekoh da podelim sa vama… možda vam zatreba nekada ovako nešto. Elem, pretpostavimo da i vi morate da izmenite jednu te istu liniju u preko 100 fajlova. (Sređivao sam prevod za Joomla! 1.5.7 pa je trebalo promeniti prvu linuju svih fajlova sa prevodom da umesto 1.5.6 stoji 1.5.7) Ima načina i načina, reče jedan mudar čovek. Tačno. I evo vam jednog krajnje jednostavnog načina da pomenutu radnju izvedete uz pomoć Perla:perl -pi -w -e 's/trazi/zameni/g;' *.ini I šta ovo radi?  Pretražuje sve fajlove sa ekstenzijom INI i u njima ovo "trazi" i umesto toga stavlja "zameni". Ili malo detaljnije:-e znači: izvrši sledeću liniju koda. -i uredi(edit) u mestu. -w prikaži upozorenja. -p  petlja(loop). Ovo je izmenilo željenu linuju u 93 fajla za par sekundi. Vodite računa da znake tipa "/" i "." morate izbeći jednim "\" ispred svakog od njih. Recimo, moja linija za izmenu verzije Joomla! u fajlovima sa prevodom bi izgledala ovako:perl -pi -w -e 's/1\.5\.6/1\.5\.7/g;' *.ini

MySQL Jokeri

April 11, 2008

Pitanje za milion dolara u nekom popularnom kvizu. Imate samo 15 sekundi za odgovor. Srecni ste jer ste izvukli pitanje iz vase omiljene oblasti :)

Kako obrisati sve korisnike ciji nick pocinje sa donjom crtom (’_').

I vas odgovor je naravno:

delete from users where username like '_%';

Vec mastate o raskalasnom zivotu daleko od kompjutera, mozda i neka jahtica pride (i sve sto ide u to lol). Glas voditelja vraca vas u surovu realnost. “Zao mi je, ovo nije tacan odgovor”. U neverici gledate vas query i milione koji su otisli u nepovrat.

Verovali ili ne, slicnu nevericu iskusio sam na svojoj kozi pre nekoliko veceri, tacnije 8. aprila. U tabeli sa nekoliko miliona generisanih domena uocen je bug (mala greska u regularnom izrazu) i nekako su generisani i domeni koji pocinju sa znakom “_”. Underscore (donja crta) naravno nije dozvoljen kod domena, bug je ispravljen ali treba obrisati i te nevalidne domene iz baze. Nista lakse, jedan brzi query i sve ce ubrzo biti pocisceno. Otvaram mysql klijent i bez mnogo razmisljanja kucam:

mysql> delete from result_domains where domain like '_%'; Query OK, 9035782 rows affected (9 min 57.35 sec)

WTF!?? Query je obrisao sve domene iz tabele. Ali kaaakooo???

I onda se setim. Donja crta - ‘_’ je poput ‘%’, takodje joker karakter koji za razliku od ‘%’ (koji menja ‘nula ili vise’ karaktera), ‘_’ menja tacno jedan karakter. Ne secam se kada sam ga poslednji put koristio (ako sam ga uopste koristio), ali znam da mi je on dosao glave.

Cimam admina na ICQ … treba mi backup, server taj i taj, tabela ta i ta, poslednji koji imamo … ASAP! Posle 30-tak sekundi admin se javlja “ok, poslednji koji imamo je od 8 marta. Gde da ti stavim?”. FUCK!!!

FUCK!!! FUCK!!! FUCK!!! FUUUUCK!!!

Ustajem od kompjutera i besno setam kroz sobu. Na postoji nacin da objasnim klijentu da sam jednim jedinim kverijem sjebao sate i sate mukotrpnog rada, podatke vredne verovatno hiljade dolara. Da imam utoku verovatno bih pao u iskusenje da pucam sebi u glavu. Mozda pre toga da sredim i admina? Kako god, sta je tu je, ne preostaje nista drugo nego da napisem email i objasnim svom klijentu sta se desilo.

Sedam ponovo za comp, ali umesto da otvorim thunderbird, cimam ponovo admina.

- Ja: “Jebote … kako se desilo da je poslednji backup star mesec dana???” - Admin: “Kako to mislis? Ovo je backup od sinoc!?” - Ja: “Rekao si 8 mart???” - Admin: “Ups. Sorry, 8. April. Sad gledam, kreiran je pre samo par sati. Moze?”

I tako, my ass has been saved. Ali moglo je i biti drugacije. Mnogo drugacije.

Pouka price:

1) Budite ekstremno oprezni kada kucate nesto unutar mysql klijenta (ili phpmyadmina) koji barata sa live podacima 2) Ako morate da brisete/update-ujete nesto, uvek uradite prvo count nad istim podacima i istom where klauzom kako bi ste se uverili da je to bas ono sto ste zeleli.

Npr:

mysql> select count(*) from result_domains where domain like '_%'; +----------+ | count(*) | +----------+ | 9035782 | +----------+ 1 row in set (5.27 sec)

(ups nesto ne valja, ovo ce obrisati sve domene koje imamo)

mysql> select count(*) from result_domains where domain like '\_%'; +----------+ | count(*) | +----------+ | 0 | +----------+ 1 row in set (4.18 sec)

Aha, sada je sve ok :)

3) Uvek pravite redovan backup. Ako to za vas rade admini postarajte se da rade svoj posao kako treba. Cak i tada nije losa ideja da s vremena na vreme napravite sopstveni backup.

P.S. Problem sa pocetka price (matchovanje stringa koji pocinje sa ‘_’) resava se jednostavnim escapovanjem specijalnog karaktera. Dakle:

select foo from footable where somefield like '\_%';

ili ako ste ljubitelj regularnih izraza:

select foo from footable where somefield regexp '^_';

Curl HTTP Client 1.2

February 15, 2008

Upravo uploadovao novu verziju sa nekim sitnim izmenama. Ispravljeno je nekoliko sitnih bugova na koje su se korisnici žalili, a od novih mogućnosti dodata je mogućnost pozivanja send_post_data metoda direktno sa post stringom, zatim podrška za gzipped contents itd.

Verziju 1.2 možete skinuti ovde.

А какав си ти ђак, мали?

September 18, 2007

Сигурно су и вама старији постављали питање из наслова док сте били школарац. Ја сам увијек био одличан, па ми није сметало. Да сам у школи сада, опет ми вјероватно не би сметало. Али сада имамо и електронске дневнике, што зацијело убија дио шарма школовања. Вјероватно је нормално да клинац понешто не каже родитељима, са електронским дневницима он нема шта да говори, родитељи све већ знају. Велики Брат их посматра.

То ме подсјети на врло ефикасан метод мога оца, који би отишао ненајвљен и непозван у моју школу једном или два пута годишње, а да ја то нисам знао. А понекад би само дошао кући и рекао: Владане, молим те напиши ми које оцјене имаш. Наравно, то се дешавало чешће од тих један или два пута када је он стварно и ишао у школу, али будући да ја нисам знао када је то било стварно, углавном би све тачно рекао како стоје ствари. Но, увијек је било простора за мување :)

Али, ово није текст о мојим школским данима, већ о клинцима који тренутно иду у основне и средње школе, а у школама имају постављене електронске дневнике. Уопште не волим да коментаришем рад других колега из ИТ индустрије, али ово се тиче безбједности мале дјеце и мора бити наглашено. На страну што ми изглед страница не одаје осјећај сигурности и знања, оно што је за узбуну су данашње информације које говоре о томе да је у систем електронских дневника могуће релативно лако неовлаштено ући, како је писано на ДПТ форуму (уз слике као доказ). Ако систем при погрешно унесеним подацима лијепо опише грешку базе података, потребно је само мало маште да би сте ушли унутра. А унутра су приватни подаци малољетне дјеце. И таква врста неодговорности и даваоца услуге и Министарства просвјете ме заиста брине. И растужује…

belgrade, programming, security

PHP 4 end of life announcement

July 14, 2007

Preuzeto sa php.net

[12-Jul-2007]

Today it is exactly three years ago since PHP 5 has been released. In those three years it has seen many improvements over PHP 4. PHP 5 is fast, stable & production-ready and as PHP 6 is on the way, PHP 4 will be discontinued.

The PHP development team hereby announces that support for PHP 4 will continue until the end of this year only. After 2007-12-31 there will be no more releases of PHP 4.4. We will continue to make critical security fixes available on a case-by-case basis until 2008-08-08. Please use the rest of this year to make your application suitable to run on PHP 5.

For documentation on migration for PHP 4 to PHP 5, we would like to point you to our migration guide. There is additional information available in the PHP 5.0 to PHP 5.1 and PHP 5.1 to PHP 5.2 migration guides as well.

Šta reći nego - Konačno! Nadam se da će ovo najzad pogurati ljude ka migraciji ka petici. A PHP6 samo što nije :)

Може ли сте покренути онлајн бизнис за један викенд?

July 9, 2007

Прошлог викенда у Колораду, САД, одржана је једна занимљива акција, СтартапВикенд (StartupWeekend), у којој 70 људи покушава да за један викенд покрене један Wеб2.0 стартап. Од великог броја понуђених идеја (од јуна је трајало слање приједлога) изабрана је једна, Воснап (Vosnap), подршка групном доношењу одлука. Или просто, врста апликације за гласање са понуђеним одговорима, уз опцију позивања на гласање.Идеја скупа је занимљива и вјерујем да су учесници доста научили и лијепо се забавили. Учесници, њих 70 су истовремено и власници пројекта. Али, ово је све играрија… Нити је испитано тржиште, нити су ти људи икада радили заједно, нити је потребно да 70 људи ради на једном оваквом пројекту и на крају, што је мени посебно занимљиво - чак нису ни успјели. Наиме, рок да се лансира бета сајт је био недјеља увече, а у тренутку док ово пишем сајт и даље не ради.Могуће је да је ово смишљена ПР акција за промоцију стартап инкубатора чији оснивачи стоје иза акције.

Свеједно, и поред тога што нису успјели у задатом року, ја вјерујем да група људи који се добро познају у послу може створити овакво нешто за један викенд. Само, што је тако брзо, мора да је и кусо.

business, internet, programming, social, web2.0

Понешто о # знаку

July 7, 2007

Појавом модернијих телефонских уређаја, а посебно рачунара, у свакодневну употребу се тихо увукао необичан знак #. Свакако ће они музички образовани примјетити да подсјећа на повисилицу, па га Грци, Французи, Румуни и Бугари зову тако, понекад и Американци (sharp), мада ови посљедњи много чешће користе ријечи pound и hash. Арапи и Данци га зову квадратом, Мађари двоструким крстом, Руси, Чеси и Словаци мрежицом, а Италијани капијицом и коначно Срби обично тарабом.

Очигледно, појаву овога знака у широј употреби није пратила и нека стандардизација, па отуд оволико шаренило назива. Тек касније, у Јуникод стандарду договорено је да званично име за знак # буде Number sign, што подсјећа на једну од употреба овога знака умјесто скраћенице No. при нумерисању. Овај знак није исто што и знак за музичку повисилицу и имају посебне стандардне ознаке и називе.

Мање познато име овога знака је октоторп (оctothorpe) или октатерп (octatherp) и сковано је у Беловим лабораторијама (Bell Labs) шездесетих година двадесетог вијека, када су стручњаци ове компаније радили на развоју тонског начина бирања за телефоне. Наиме, тада се указала потреба за увођењем два додатна тастера, звјездице * и тарабе #. Оба знака су и данас присутна на тастатурама телефона. Док за звјездицу није било пуно недоумица како је назвати (иако су изабрали ријеч star, а не asterisk), за именовање знака # су морали да укључе машту. Окто, први дио ријечи је позајмљен из грчког назива за број осам (као октопод, што ме подсјети на мени драг стари словенски назив за ову животињу - осминог, који и даље користе народи који више пазе на свој језик од нас), јер знак има осам кракова. За други дио ријечи не постоји сигуран извор о томе шта је. Свеједно, ово име, иако се сачувало, није никада ушло у ширу употребу.

Занимљива је била и расправа око назива овога знака у вријеме појаве новог Мајкрософтвог језика C#. Наиме, Мајкрософт је језик назвао Си-Шарп, као “повишени Ц”, али будући да на тастатурама не постоји знак за повисилицу, за ознаку језика су искористили сличан знак #, стварајући забуну да ли се језик зове Си-Шарп или Си-Хеш. Језик је стекао популарност и данас нема недоумица око његовог назива, али језичких недоумица са знаком # ће засигурно бити и даље. Само код програмера нема недоумица, погрешна употреба овога знака у већини програмских језика резултираће грешком :) А за шта се све користи овај знак погледајте на Википедији.

history, language, microsoft, programming

MySQL 5.x - Konačno unapređen klijent

June 21, 2007

Listajući svoje omiljene WebDev feed-ove, za oko mi je zapao jedan post na sjajnom MySQL Perfomance Blogu:

…if you press CTRL-C MySQL Command Line Client will not exit but will terminate query being executed.

Drugim rečima, u dosadašnjim verzijama MySQL klijenta, kada bi ste ukucali neki query i pritiskom na CTRL-C pokušali da prekinete njegovo izvršavanje, CTRL-C bi zapravo ubio MySQL klijent, ali query nastavlja da se izvršava u pozadini! Rešenje u takvim slučajevima je traženje id-a tog query-a na listi procesa upitom “show full processlist”, a zatim njegovo “ubijanje” upitom “kill 12345″ gde je 12345 u ovom slučaju id procesa koji želimo da ubijemo. Dakle, recimo nešto ovako:

mysql> select * from odm_result_keywords where keyword like '%foo%joe%'; ^CAborted bash-2.05b$ mysql -A --enable-local-infile -udinke -ppass mydb Welcome to the MySQL monitor. Commands end with ; or g. Your MySQL connection id is 1512 to server version: 4.1.18-log Type 'help;' or 'h' for help. Type 'c' to clear the buffer. mysql> show full processlist; +------+-------+----------------------------+-------------------+---------+------+--------------+--------------------------------------------------------------------+ | Id | User | Host | db | Command | Time | State | Info | +------+-------+----------------------------+-------------------+---------+------+--------------+--------------------------------------------------------------------+ | 1486 | dinke | localhost | mydb | Query | 3 | Sending data | select * from odm_result_keywords where keyword like '%foo%joe.cl' | +------+-------+----------------------------+-------------------+---------+------+--------------+--------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> kill 1486; mysql>

Zahvaljujući izmenama u MySQL klijentu, sada je dovoljno da stisnete CTRL-C, i query će biti odmah prekinut:

mysql> select domain from odm_result_keywords_de where whois_status is null and domain like '%.%.%'; Query aborted by Ctrl+C ERROR 1317 (70100): Query execution was interrupted mysql>

Više informacija o ovoj, kao i drugim izmenama u pomenutoj verziji (5.0.25) možete naći ovde.

MySQL - Backup velikih MyISAM tabela

June 20, 2007

Ako ste ikada morali da backup-ujete ili prebacujete podatke iz jedne ili više MySQL tabela, znate da se za to koristi mysqldump utility, pomoću kojeg jednostavno podatke iz MySQL-a “dumpujete” u neki mojetabele.sql fajl, koji zatim možete sačuvati kao backup, prebaciti i importovati na neki drugi MySQL server, itd. Ovu proceduru koristio sam milion puta do sada, i ona se generalno svodi na izvršavanje sledećih komandi u linuxu:

export:

mysqldump -udinke -pmojpass -hhostname.prvog.servera ime_baze tabela1 tabela 2 tabelan > dump_fajl.sql

import:

mysql -udinke -pmojpass -hhostname.drugog.servera ime_baze

Tu i tamo pojavi se problem kada prebacujete dump sa novije verzije MySQL-a na stariju, i tada je poželjno specifirati i odgovarajući compability flag prilikom izvršavanja mysqldump komande (–compatible=name gde name može biti mysql323, mysql40, postgresql, oracle itd.).

Elem, pre nekoliko dana zbog prebacivanja MySQL servera sa jedne lokacije na drugu, morao sam da odradim dump solidno velikih MySQL tabela (10-tak tabela gde je svaka imala preko 2 miliona slogova). Nakon užasavajuće duge procedure dumpa, gzipovanja i scpovanja na drugi server, konačno sam startovao import. Međutim, nakon 3 sata (tri sata) umesto standardne “no news is a good news” poruke, kada sam bacio pogled na status dočekala me je sledeća poruka …:

[dinke@um-917 ~/public_html]$ mysql -udinke -p325ewfwt23rasf keyword_discovery

WTF? Prebacivao sam podatke sa starije verzije na noviju, što znali da import mora proći glatko! Ovo bi trebalo da znači da je MySQL negde nešto pobrljavio sa indexima, i umesto da čekam još 2 sata da se odradi check & repair, odlučujem se da napravim ponovo dump fajl, ovaj put sa ignore opcijom, kako bi svi inserti u dump fajlu bili “insert ignore” tako da u slučaju ovakve greške ne bi došlo do pucanja. Ne preterano pametno, ali podaci nisu extremno osetljivi i mogu da dozvolim luksuz da izgubim desetak slogova, ali ne i desetak sati za import!

Naravno, novi dump, zip, scp … to je traajalooo … toliko dugo da sam posle par sati nakon startovanja importa krenuo da mozgam sa administratorom (kuki) oko alternativnih rešenja. A rešenje je bilo jednostavno ali efikasno. Prekopirali smo sve MySQL data fajlove sa jednog servera na drugi (*.MYI, *.MYD i *.frm fajlove), a zatim odradili myisamchk kako bi sredili pomenute tabele, jer nismo smeli da spuštamo server prilikom backup-a što generalno može da znači dosta problema ako se na ovaj način backupuju fajlovi.

Sve u svemu, ova operacija prošla je jako brzo (najduže je trajalo kopiranje fajlova sa jednog hosta na drugi), i za manje od pola sata cela procedura bila je završena.

Zaključak do koga smo došli je da je kod velikih tabela dumpovanje praktično nepihvatljivo kao backup rešenje, i da se samim tim kao jedino rešenje nameće kopiranje MySQL data fajlova. Kod MyISAM tabela to nije nikakav problem, jer su one “platform safe”, tj. binarni fajl kreiran na jednoj platformi (recimo *.MYI fajl na linuxu) radiće bez ikakvih problema na drugoj platformi (recimo isti *.MYI fajl na Windowsu).

Posao za PHP Programere

April 25, 2007

Juče me je po ko zna koji put moj klijent iz USA pitao za neke nove programere, po zna koji put sam stavio oglase na DPT i ES, i po ko zna koji put sam dobio jednu lepu okruglu cifru CV-eva. Nula, nada, ništa. Inače, kada sam postavio oglas na DPT, moj prijatelj Bluesman mi je ironično rekao da “ako nađeš takvoga, ja mu plaćam pola plate”. Obavešten čovek, nema šta. Slično je prošao i b92.net u svom oglasu za administratora sajta, a detaljnije o tome piše Dejan Bizinger.

No, neću više da kukam kako u Srbiji nema dobrih programera (ili bar onih kojima treba posao). Rešio da testiram blogosferu i postavim ponudu na posao ovde, i tako testiram moć naših cenjenih agregatora srpsko-hrvatske blogosfere :).

Evo ponude:

Lampix u saradnji sa svojim USA partnerom First Beat Media potražuje iskusne Programere/Developere koji zadovoljavaju sledeće uslove:

Pozicija 1: PHP Programer

- Minimum 2 godine iskustva u radu sa PHP-om - Odlično poznavanje MySQL-a. Iskustvo u dizajnu i implementaciji baza podataka. - Poznavanje principa Objektno Orijentisanog programiranja. - Iskustvo u razvoju AJAX aplikacija je plus. - Poznavanje Linuxa je plus. - Komunikativnost, posvećenost poslu, timski rad, kreativnost. - Sposobnost da se ispoštuju rokovi. - Želja za stalnim usavršavanjem. - Tečan engleski - Fakultetska diploma je plus

Pozicija 2: PHP Network Programmer

- Poseduje minimum 2 godine iskustva u radu sa PHP-om - Odlično poznaje MySQL i ima iskustva u optimizaciji istog. - Poznaje principe Objektno Orijentisanog programiranja i zna da iste primeni u praksi. - Poznaje *nix (minimum u meri da moze sam da se uloguje u shell, edituje fajlove i sl.) - Poznaje regularne izraze - Ima iskustva u network programiranju, bilo koriscenjem socketa ili Curl-a. - Komunikativnost, posvećenost poslu, timski rad, kreativnost. - Sposobnost i odgovorost da se ispoštuju zadati rokovi. - Želju za stalnim usavršavanjem. - Tečan engleski - Fakultetska diploma je plus

Zadatak potencijalnog kandidata (ako takav postoji u ovoj zemlji) bio bi razvoj i unapredjenje postojecih CLI PHP skriptova koje se koriste za neku vrstu data mininga, gde je neophodno spiderovati odredjene sajtove i pritom raditi bindovanje na vise adresa, multithreading (tacnije multiplexing) itd. Zbog kolicine podataka neophodno je i da se odlicno snalazi sa (relativno) velikim tabelama (par desetina miliona slogova) i optimizacijom istih.

U slučaju da zadovoljavate gornje uslove, molim vas pošaljite Vaš CV na email jobs@lampix.net (Nepotpune i neozbiljne prijave neće biti uzete u razmatranje).

Odabranim kandidatima biće ponuđen stalan posao uz atraktivnu platu i odlične radne uslove. Prednost imaju kandidati koji mogu da rade puno radno vreme u Lampix office-u u Kragujevcu, ali je za kvalitetne kandidate sa stalnom Internet konekcijom moguć rad i od kuće.

Inače, radi se o poslu koji se kao što rekoh može raditi i od kuće (ne živim u iluziji da će pojaviti neko iz Kragujevca), uz odličnu platu i nadasve opuštenu atmosferu. U slučaju da zadovoljavate gornje uslove i pritom želite da postanete deo ovog jakog multinacionalnog “creme de la creme” tima, šta čekate?

Client size resizovanje slika

March 12, 2007

Radeći na servisu Blogodak, nedavno sam se susreo sa problemom kod slika velikih dimenzija koje neki korisnici uključuju u svojim feedovima. Naime, kako Blogodak “vuče” feed-ove sa raznih domaćih blogova, a sa njima i slike koje se u njima nalaze, dešavalo se da one dimenzija većih od 560px uredno “skrljaju” layout blogotka, obzirom da to e == “Microsoft Internet Explorer”) {prevazilazi predviđenu veličinu containera, tako da u najboljem slučaju dolazi do pojave horizonatalnog skrol bara. Kako nemamo nikakav uticaj na slike koje se vuku sa servera gde su hostovane, server side varijante(npr. resize korišćenjem GD liba ili Image Magicka) nisu primenjive, jedino rešenje je da se dimenzije slike smanje direktno u browseru - Client Side.

CSS Rešenje Za browsere koji imaju potpunu podršku za css2, dovoljno je staviti nešto tipa:

.container img{ max-width:560px; }

u stil strane, tako da će sve slike koje se nalaze unutar nekog <div id=”container”>…</div> elementa biti ograničene na max 560 piksela, tj. biće automatski resizovane na odgovarajuću veličinu. Naravno, ovo ne radi u IE-u (verzije

IE Hack (CSS verzija)

.container img{ max-width:560px; /*hack for IE*/ width: expression(this.width > 560 ? 560: true); }

Ovaj css hack je validan samo u IE-u, koristi se neka vrsta “ternarnog operatora”, a sam hack skinut je sa ovog bloga. Rado bih vam rekao da više informacija potražite tamo, ali i sam autor priznaje da na razume mnogo oko toga “kako to radi”, ali jednostavno radi. Za ljubitelje standardnijih rešenja, sledi JS verzija.

IE Hack (JS verzija)

Obzirom da mi se nije svidela ideja da koristim nevalidan css kod koji uz to i ne razumem u potpunosti(na stranu što je pravio i neke nekoegzistentne probleme sa IE-om za koji je i namenjen), odlučio sam se za JavaScript rešenje koje sledi.

function fixImages() { //fix images for ie only if(navigator.appName == "Microsoft Internet Explorer") { for(i=0; i<document.images.length; i++) { //if image is bigger than 560 if(document.images[i].width > 560) { imgRatio = document.images[i].width/document.images[i].height; document.images[i].width = 560; document.images[i].height = 560 / imgRatio; //hack neophodan da bi se uklonio skroler zbog prvobitne velicine slike //mainContent je ime div kontejnera koji drzi sadrzaj strane divid = document.getElementById('mainContent'); content = divid.innerHTML; divid.innerHTML = ''; divid.innerHTML = content; } } } }

Ova funkcija se poziva nakon učitavanja stane(onLoad). U slučaju da je u pitanju IE, proveravaju se dimenzije svih slika na strani(parsuje se niz document.images), i u slučaju da dimenzije prevazilaze 560 piksela, setuju se na manje, uz očuvanje proporcija slike. Naravno, ovaj metod ima manu, jer je neophodno da se sve slike prvo učitaju, pa tek onda dolazi do resizovanja. Kod IE-a i pored resizovanja slike na “prihvatljive” dimenzije, bilo je potrebno nekako mu i staviti do znanja da je došlo do promene dimenzija(samo resizovanje nije dovoljno da nestane horizontalni skroler), tako da je bilo neophodno ručno ili skriptabilno rezisovati i prozor browsera. Srećom posle manjeg “prčkanja” sa alternativama prošlo je i jednostavnije rešenje sa setovanjem kontejnera cele strane na prazan string i vraćanjem na prvobitno stanje korišćenjem innerHTML-a.

Nadam se da će ovo nekome biti od koristi :)

Curl HTTP Client - Update

March 1, 2007

Upravo sam postavio novu verziju svoje Curl_HTTP_Client klase. Nove mogućnosti uključuju file download/upload, korišćenje proxya itd.

Novu verziju možete skinuti sa PHP Classes kao i sa mog sajta - ovde. Opis svih mogućnosti možete naći ovde.

Kako Prepoznati Srbina

December 16, 2006

Sećate li se mog prijatelja Manu-a, amerikanca koji prati isključivo fudbal(i to ne američki) ?

Manu 15.12.20 20:34 usa stole a serb footballer Manu 15.12.20 20:35 http://chivas.usa.mlsnet.com/players/bio.jsp?team t120&player=kljestan_s&playerId=kle326299&statTyp =current dinke 15.12.20 20:35 Sacha Kljestan ? Serb ? Manu 15.12.20 20:36 lol Manu 15.12.20 20:36 you should know Manu 15.12.20 20:36 that is serb name Manu 15.12.20 20:36 that is not serb name Manu 15.12.20 20:36 that is serb name Manu 15.12.20 20:36 that is not serb name Manu 15.12.20 20:36 to be or not to be a serb Manu 15.12.20 20:36 that is the question

Nakon što mi je sinoć po n-ti put(gde n–>00) servirao vest o nekom mom “zemljaku”, morao sam da potrošim vreme i objasnim mu kako da prepozna Srbina u vestima(tim pre što u našem multinacionalnom timu imamo dvocifren broj ovdašnjih programera):

function isSerb($lastname) { $found = preg_match("/^[\pL]+ić$/u", $lastname); if($found) { return true; } else { return false; } }

Uvod u GeoIP

November 27, 2006

Verovatno ste već bili u prilici da koristite Google Analytics alat, gde između ostalog možete na mapi sveta videti odakle tačno dolaze posetioci vašeg sajta, ili ste tu i tamo posetili sajt koji bi Vam između ostalog izbacio podatke o Vašoj trenutnoj lokaciji. Naravno, nije u pitanju nikakva magija, tačna lokacija posetioca definisana je na osnovu njegove IP adrese, a tehnologija koja se koristi prilikom “lociranja” korisnika opšte je poznata pod nazivom GeoIP.

Danas ćemo pričati o tome kako “locirati” posetioca pomoću PHP-a i Max Mind-ove GeoIP baze. U primerima koji slede koristicemo besplatne(lite) verzije GeoIP baza, obzirom da se za pune verzije plaća $50USD + $12USD za update (GeoIP Country baza) i $370USD + $90USD za update (GeoIP City baza). Mana lite verzija je što nisu uvek 100% ažurne, ali će odlično poslužiti za naš tutorijal, a iz ličnog iskustva tvrdim da su upotrebljive i u većini live projekata.

MaxMind obezbeđuje API za nekoliko popularnih programskih jezika, (kompletna lista dostupna je ovde), a detalji o PHP API-u dostupni su ovde. Pored takozvanog “Pure PHP API-a” koji ćemo ovde koristiti, postoje i PECL ektstenzija kao i apache modul(mod_geoip), koji pružaju bolje perfomanse ali i komplikovaniji setup.

Za početak neophodno je da skinete sve fajlove koji se nalaze na http://www.maxmind.com/download/geoip/api/php/ i snimite ih negde unutar vašeg Web stabla(recimo /htdocs/geoip). Za korišćenje GeoIP Country treba skinuti lite bazu odavde, a za city GeoLiteCity bazu odavde. Radi jednostavnosti korišćenja, obe baze ćemo takođe raspakovati u isti direktorijum gde smo i snimili fajlove iz PHP API-a (/htdocs/geoip).GeoIP Country ——————————–

Idemo sa primerom detekcije zemlje posetioca:

<?php /** * Primer Koriscenja GeoIP Country Baze * * @version $Id$ * @package geoip * @copyright © 2006 Lampix.net * @author Dragan Dinic <dinke@lampix.net> */ require_once("geoip.inc"); $gi = geoip_open("GeoIP.dat", GEOIP_STANDARD); $ip = $_SERVER['REMOTE_ADDR']; //ako testirate u lokalu koristite ovaj ip radi testa //posto ce $_SERVER['SERVER_ADDR'] biti 127.0.0.1 //$ip = "89.216.226.174"; $country_name = geoip_country_name_by_addr($gi, $ip); $country_code = geoip_country_code_by_addr($gi, $ip); if($country_name) { echo "Zemlja iz koje nas posecujete je: $country_name <br />"; echo "Skracena Oznaka: $country_code <br />"; } else { echo "Nazalost, nismo bili u mogucnosti da vas lociramo."; } geoip_close($gi); ?>

Dakle, na početku uključujemo geoip.inc koji sadrži sve f-je potrebne za korišćenje GeoIP County baze, zatim kreiramo novu instancu GeoIP klase pomoću geoip_open f-je, i na kraju pozivamo odgovarajuće f-je (geoip_country_name_by_addr i geoip_country_code_by_addr) da bi smo dobili ime/kod zemlje u kojoj se nalazi ip adresa posetioca(u slučaju da testirate u lokalu nemojte koristiti $_SERVER[’REMOTE_ADDR’]).

Kao izlaz skripta, trebalo bi da dobijemo nešto poput:

Zemlja iz koje nas posecujete je: Serbia and Montenegro Skracena Oznaka: CS

F-je koje smo koristili da bi dobili podatke o zemlji posetioca, samo su neke od f-ja koje su dostupne u API-u. Ostatak možete i sami pronaći jednostavnom analizom PHP sourca geoip.inc fajla.

GeoIP City —————————-

A sada da proširimo podatke o zemlji sa tačnom lokacijom (grad, poštanski kod itd).

<?php /** * Primer Koriscenja GeoIP City Baze * * @version $Id$ * @package geoip * @copyright © 2006 Lampix.net * @author Dragan Dinic <dinke@lampix.net> */ require_once("geoipcity.inc"); $gi = geoip_open("GeoLiteCity.dat", GEOIP_STANDARD); $ip = $_SERVER['REMOTE_ADDR']; //ako testirate u lokalu koristite ovaj ip radi testa //posto ce $_SERVER['SERVER_ADDR'] biti 127.0.0.1 //$ip = "89.216.226.174"; $record = geoip_record_by_addr($gi, $ip); if(!$record) { echo "Nazalost, nismo bili u mogucnosti da vas lociramo."; } else { echo "Zemlja: " .$record->country_name . "<br />"; echo "Skracena Oznaka: " . $record->country_code . "<br />"; echo "Skracena Oznaka2: " . $record->country_code3 . "<br />"; echo "Region: " .$record->region . "<br />"; echo "Grad: " .$record->city . "<br />"; echo "Postanski Kod: " .$record->postal_code . "<br />"; echo "Geog. Sirina: " .$record->latitude . "<br />"; echo "Geog. Duzina: " .$record->longitude . "<br />"; } geoip_close($gi); ?>

Kao što vidite, PHP kod je sličan kodu za detekciju zemlje, s tim što smo koristili geoipcity.inc kao i GeoLiteCity.dat bazu. F-ja geoip_record_by_addr($gi, $ip) vraća instancu klase ‘geoiprecord’ koja sadrži kao promenljive(osobine) podatke o lokaciji koje koristimo u gornjem kodu. Nakon pokretanja skripta trebalo bi da dobijemo nešto poput:

Zemlja: Serbia and Montenegro Skracena Oznaka: CS Skracena Oznaka2: SCG Region: 02 Grad: Beograd Postanski Kod: Geog. Sirina: 44.8186 Geog. Duzina: 20.4681

Napominjem da je GeoIP baza najažurnija kada su u pitanju gradovi sa severnoameričkog dela planete, dok je njena preciznost znatno manja kada se dođe do “egzotike” u koju nažalost spada i Srbija.

CaseStudy - Redirekcija na osnovu IP adrese ——————————————– Za kraj znanje stečeno ovde iskoristićemo u jednom pravom projektu. Naime cilj je da se na dvojezičnom sajtu(blogu) korisnici koji dolaze iz Srbije usmere na srpsku verziju sajta, dok će se svi ostali usmeriti na englesku verziju. Evo kako to izgleda:

<?php /** * Case Study - Redirekcija na osnovu lokacije * * @version $Id$ * @package geoip * @copyright © 2006 Lampix.net * @author Dragan Dinic <dinke@lampix.net> */ require_once("geoip/geoip.inc"); $gi = geoip_open("geoip/GeoIP.dat",GEOIP_STANDARD); $country_code = geoip_country_code_by_addr($gi, $_SERVER['REMOTE_ADDR']); geoip_close($gi); if($country_code == 'CS') { header("HTTP/1.1 301 Moved Permanently"); header('Location: http://www.dinke.net/blog/sr/'); } else { header("HTTP/1.1 301 Moved Permanently"); header('Location: http://www.dinke.net/blog/en/'); } ?>

Primer koji vidite gore koristi se upravo na ovom blogu, kako bi sve korisnike koji ne dolaze iz Srbije automatski preusmerio na englesku verziju bloga. Slanje custom 301 redirection headera je važno kako bi botovi (Google i sl.) indeksirali strane na odgovarajući način.

Izašao PHP 5.2.0

November 3, 2006

Nova verzija PHP-a, 5.2.0 od juče je dostupna za download.

Pored gomile ispravljenih bagova, nova verzija donosi i neke interesantne mogućnosti od kojih su meni najinteresantnije podrška za “progres bar” prilikom uploada, kao i podrška za zip fajlove. Više informacija o tome šta nam novo donosi PHP 5.2 možete pronaći ovde.

MySQL - Kako ukloniti duplikate

September 21, 2006

Ovaj post je donekle inspirisan problemom koji je bluesman opisao na svom blogu. Dva problema koji nemaju veze jedan sa drugim, ali odlično pokazuju zašto poslodavci insistiraju na x godina iskustva (gde x > 2) :)

Naime, danas sam “u minut do dvanaest” dobio fajl sa par stotina hiljada keyworda koje treba procesirati ASAP (oh kako volim ovu reč). Naravno, u žurbi nisam mnogo gledao u fajl, provalio sam da je format uobičajen (1 keyword po liniji), importovao u bazu sa load data local infile … , startovao procesiranje i to bi bilo to.

Par minuta nakon toga startuje me kolega koji je zaboravio da mi kaže da se kod nekih keyworda “potkralo” par domena tipa “foo.eu” gde ono “.eu” samo treba izbaciti. Sve je to lepo, ali svi su već importovani u tabelu i samo što nisu pokupljeni. Rešen da brzo delam, odlučim da je najjednostavnije odraditi “search/replace hack” direktno u tabeli, ali …

mysql> update odm_master_keywords set keyword = replace(keyword, '.eu',''); ERROR 1062 (23000): Duplicate entry 'academia' for key 2

Shit. Posto je keyword polje unique, neki od tih “.eu” je nakon search/replace-a napravio problem sa istim identičnim keywordom. Ok, obrisaću ručno te duplikate, valjda ih nema mnogo …

mysql> delete from odm_master_keywords_cl where keyword='academia'; Query OK, 1 row affected (0.02 sec) mysql> update odm_master_keywords set keyword = replace(keyword, '.eu',''); ERROR 1062 (23000): Duplicate entry 'academic' for key 2 mysql> delete from odm_master_keywords_cl where keyword='academic'; Query OK, 1 row affected (0.02 sec) mysql> update odm_master_keywords set keyword = replace(keyword, '.eu',''); ERROR 1062 (23000): Duplicate entry 'actriz' for key 2 mysql> delete from odm_master_keywords where keyword='actriz'; Query OK, 1 row affected (0.02 sec) mysql> update odm_master_keywords set keyword = replace(keyword, '.eu',''); ERROR 1062 (23000): Duplicate entry 'foo' for key 2 mysql> delete from odm_master_keywords where keyword='foo'; Query OK, 1 row affected (0.48 sec) ...

I tako, mogao bih ovako do prekosutra … Mora da postoji nešto pametnije :)

Nakon kraćeg razmišljanja, došao sam do ovog rešenja. Privremeno skloniti “unique” index, odraditi update i vratiti index natrag, ali sa ignore opcijom koja će “ubiti” višak duplikata. Dakle:

mysql> alter table odm_master_keywords drop key keyword; Query OK, 338565 rows affected (13.79 sec) Records: 338565 Duplicates: 0 Warnings: 0 mysql> update odm_master_keywords set keyword = replace(keyword, '.eu',''); Query OK, 378 rows affected (4.95 sec) Rows matched: 338565 Changed: 378 Warnings: 0 mysql> alter ignore table odm_master_keywords add unique keyword(keyword); Query OK, 338565 rows affected (46.45 sec) Records: 338565 Duplicates: 233 Warnings: 0

Obratite pažnju na ignore deo u poslednjem alteru. On je ključan ovde, jer da ga nismo koristili ponovo bi došlo do greške(duplicate entry …) prilikom update-a.

Inače, poslednja dva query-a su uobičajen “pattern” kada se želite osloboditi duplikata u nekom polju - jednostavno dodajte unique index na polje i višak će biti obrisan iz tabele.

Python 2.5

September 19, 2006

I just went over to the Python website to check out in more depth the new features of Python 2.5 when I got a sweet surprise – Python 2.5 FINAL is out!

You can download it here, read the highlights and of course what’s new.

Enjoy!

Curl HTTP Client

September 12, 2006

Vrlo često u svojim svakodnevnim poslovima imam potrebu da parsujem podatke sa raznih sajtova (tipa lista expired domena, lista blogova na planetoidu i sl.) i to najčešće tamo gde nemam RSS na raspolaganju. Dugo vremena koristio sam sopstvenu HTTP klasu baziranu na soketima, ali nisam nešto preterano bio zadovoljan perfomansama, plus sam bio suočen sa gomilom drugih problema tipa kompatibilnost izmešu linuxa i bsd-a i sl.

U međuvremenu sam tu i tamo koristio Curl cli program (uglavnom za dabagovanje), ali mi se nikada nije preterano sviđao njegov PHP API, način na koji se setuje opcije i sl. Zbog svega gore navedenog, napravio sam sopstveni curl “oop wrapper” - jednostavnu klasu koja bi trebala da znatno olakša uobičajene zadatke kao što su slanje GET/POST zahteva, snimanje kukija i sl.

Obzirom da je još nekoliko kolega ovu klasu smatralo vrlo korisnom (neki su mi čak slali i dodatne mogućnosti), pre nekoliko nedelja odlučio sam da je submitujem na phpclasses.org kao Open Source projekat (BSD Licenca).

Evo nekoliko primera korišćenja. Klasa (kao i fajl sa primerima) se mpže downloadovati ovde.

<?php /** * @version $Id$ * @package dinke.net * @copyright © 2005 Dinke.net * @author Dragan Dinic */ require_once("curl_http_client.php"); $curl = &new Curl_HTTP_Client(); //pretend to be IE6 on windows $useragent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"; $curl->set_user_agent($useragent); //uncomment next two lines if you want to manage cookies //$cookies_file = "/tmp/cookies.txt"; //$curl->store_cookies($cookies_file); //Uncomment next line if you want to set credentials //$curl->set_credentials($username, $password); //Uncomment next line if you want to set specific referrer //$curl->set_referrer("http://my.referrer.url"); //if you want to send some post data //form post data array like this one $post_data = array('login' => 'pera', 'password' => 'joe', 'other_foo_field' => 'foo_value'); //and send request to http://www.foo.com/login.php. Result page is stored in $html_data string $html_data = $curl->send_post_data("http://www.foo.com/login.php", $post_data); //You can also fetch data from somewhere using get method! //Fetch html from url $html_data = $curl->fetch_url("http://www.foo.com/foobar.php?login=pera&password=joe&other_foo_field=foo_value"); //if you have more than one IP on your server, //you can also bind to specific IP address like ... //$bind_ip = "192.168.0.1"; //$curl->fetch_url("http://www.foo.com/login.php", $bind_ip); //$html_data = $curl->send_post_data("http://www.foo.com/login.php", $post_data, $bind_ip); ?>