> Veritabanı Yönetim Sistemleri (VTYS) : Her ne kadar sistem programlamanın doğrudan veritabanlarıyla bir ilgisi yoksa da C programcılarının yine de sistem programcıların bazı durumlarda veritabanları oluşturup onları kullanması gerekebilmektedir. Bu bölümde C'den SQL kullanarak veritabanlarıyla nasıl işlem yapılacağı üzerinde duracağız. Eskiden veritabanı işlemleri kütüphanelerle yapılıyordu. Daha sonra veritabanı işlemleri için özel programlar geliştirildi. Veritabanı işlemlerini ayrıntılı ve etkin bir biçimde gerçekleştiren yazılımlara "Veritabanı Yönetim Sistemleri (VTYS/DBMS)" denilmektedir. Günümüzde çeşitli firmalar ve kurumlar tarafından geliştirilmiş pek çok VTYS vardır. Bunların bazıları kapalı ve ücretli yazılımlardır. Bazıları ise açık kaynak kodlu ve ücretsiz yazılımlardır. En çok kullanılan VTYS yazılımları şunlardır: -> IBM DB2 (Dünyanın ilk VTYS'sidir.) -> Oracle (Oracle firmasının en önemli ürünü.) -> SQL Server (Microsoft firmasının VTYS'si.) -> MySQL (Açık kaynak kodlu, ancak Oracle firması satın aldı ve gelecekteki durumu tartışmalı.) -> MariaDB (Açık kaynak kodlu, MySQL Oracle tarafından satın alınınca kapatılma tehlikesine karşı MySQL varyantı olarak devam ettirilmektedir.) -> PostgreSQL (Açık kaynak kodlu, son yıllarda geniş kesim tarafından kullanılan VTYS.) -> SQLite (Gerçek anlamda bir VTYS değil, VTYS'yi taklit eden mini bir kütüphane gibi. Bu tür yazılımlara "gömülü VTYS" de denilmektedir.) -> Microsoft Access Jet Motoru (Bu da Microsoft'un gömülü bir VTYS sistemidir. Microsoft Access tarafından da kullanılmaktadır.) Bir yazılımın VTYS olabilmesi için onun bazı gelişmiş özelliklere sahip olması gerekir: -> VTYS'ler kullanıcılarına yüksek seviyeli bir çalışma modeli sunmaktadır. -> VTYS'ler genellikle dış dünyadan istekleri "SQL (Structured Query Language)" denilen dekleratif bir dille almaktadır. Yani programcı VTYS'ye iş yaptırmak için SQL denilen bir yüksek seviyeli dekleratif bir dil kullanmaktadır. VTYS SQL komutlarını alıp onları parse eder ve C ve C++ gibi dillerde yazılmış olan motor kısmı (engine) tarafından işlemler yapılır. SQL, veritabanı işlemlerini yapan bir dil değildir. Programcı ile VTYS arasında yüksek seviyeli iletişim için kullanılan bir dildir. VTYS'lerin motor kısımları genellikle C ve C++ gibi sistem programlama dilleriyle yazılmaktadır. -> VTYS'ler pek çok yüksek seviyeli araçlara da sahiptir. Örneğin backup ve restore işlemlerini yapan araçlar her VTYS'de bulunmaktadır. -> VTYS'ler genellikle birden fazla kullanıcıya aynı anda hizmet verecek biçimde client-server mimarisine uygun biçimde yazılmaktadır. Bunlara uzaktan erişilebilmekte ve aynı anda pek çok kullanıcı bunlara iş yaptırabilmektedir. -> VTYS'ler ileri derece güvenlik sunmaktadır. Bir kullanıcı başka bir kullanıcının bilgilerine erişememektedir. -> VTYS'ler yüksek miktarda kayıtlardan oluşan veritabanları üzerinde etkin bir biçimde işlemler yapabilmektedir. Sistem programlama uygulamalarında bazen küçük veritabanlarının oluşturulması gerekebilmektedir. Bu tür durumlarda kapasiteli VTYS'ler yerine tek bir dosyadan oluşan adeta bir kütüphane biçiminde yazılmış olan gömülü VTYS'lerden (embedded DBMS) faydalanılmaktadır. Bunların en çok kullanılanı SQLite denilen gömülü VTYS'dir. Örneğin bir tarayıcı yazdığımızı düşünelim. Son ziyaret edilen Web sayfalarının bir biçimde tarayıcıdan çıkıldıktan sonra saklanması gerekir. İşte bu tür durumlarda SQLite gibi basit yapıda VTYS'ler tercih edilebilmektedir. Internet bağlantısı olmayan mobil cihazlarda da SQLite gibi gömülü VTYS'ler çokça kullanılmaktadır. Örneğin biz bir soket uygulaması yazmış olalım. Bu uygulama bir log tutacak olsun. Burada SQLServer, MySQL gibi büyük çaplı VTYS'ler yerine SQLite gibi bir gömülü VTYS'yi tercih edebiliriz. Gömülü VTYS'ler büyük çaplı veritabanlarında iyi bir performans gösterememektedir. Bunlar daha çok küçük ve orta çaplı veritabanlarında kullanılmaktadır. Veritabanı işlemleri için C'den ziyade yüksek seviyeli diller tercih edilmektedir. Örneğin Java, C#, Python gibi diller veritabanı işlemlerinde oldukça yaygın kullanılmaktadır. Benzer biçimde JavaScript de Web uygulamalarında veritabanları üzerinde işlem yapmak için kullanılan dillerdendir. Günümüzde veritabanlarının en çok kullanıldığı uygulamalar Web uygulamalarıdır. Bir Web mağazasına girdiğinizde oradaki bütün ürünler veritabanlarında tutulmaktadır. VTYS'lerin client-server mimarisine uygun bir biçimde yazıldığını belirtmiştik. VTYS'lerle tipik çalışma şu biçimdedir: -> Programcı "kullanıcı ismi" ve "parola" ile VTYS'ye bağlanır. Bu durumda programcı client durumunda, VTYS ise server durumundadır. -> Programcı VTYS'ye yaptırmak istediği şeyleri SQL dilinde oluşturur ve VTYS'ye SQL komutlarını gönderir. -> VTYS bu SQL komutlarını parse eder ve istenilen işlemleri yapar, programcıya işlemin sonuçlarını iletir. -> Programcı işi bittiğinde bağlantıyı kapatır. Her ne kadar SQLite ve Microsoft Access Jet Motoru gibi VTYS'ler aslında client-server çalışmıyor olsa da bunlar çalışma biçimi olarak geniş kapasiteli VTYS'leri taklit etmektedir. Veritabanları tasarım bakımından birkaç gruba ayrılmaktadır. Günümüzde en çok kullanılan veritabanı mimarisine "İlişkisel Veritabanı (Relational Database) Mimarisi" denilmektedir. İlişkisel veritabanları "tablolardan (tables)", tablolar da sütun ve satırlardan oluşmaktadır. Örneğin biz öğrencilerin bilgilerini tutmak için bir veritabanı tablosu oluşturalım. Bu tablo aşağıdaki görünümde olsun: Adı Soyadı Numarası Sınıfı --------------------------------------- Ali Serçe 1234 3B Güray Sönmez 6745 2C Ayşe Er 6234 2B ... Tablolardaki sütunlara "alan (field)", satırlara ise "satır (row)" ya da "kayıt (record)" denilmektedir. MySQL, SQLServer, Oracle, SQLite gibi VTYS'ler ilişkisel veritabanı mimarisini kullanmaktadır. Hiyerarşik bilgileri (örneğin bir ağaç yapısını) tutmak için hiyerarşik veritabanı mimarileri kullanılabilmektedir. Son 15 senedir ismine "nosql" denilen ilişkisel olmayan ve özellikle metin tabanlı bilgiler üzerinde işlem yapan veritabanı mimarileri sıkça kullanılır hale gelmiştir. Ancak en yaygın kullanılan mimari ilişkisel veritabanı mimarisidir. Şimdi de yukarıda kabaca açıkladığımız MySQL, SQLite gibi VTYS'lerini açıklayalım: >> "MySQL" : MySQL'i kurmak için tek yapılacak şey server programı http://dev.mysql.com/downloads/ sitesinden indirip yüklemektir. Kurulum oldukça basittir. Birtakım sorular default değerlerle geçilebilir. Ancak kurulum sırasında MySQL kurulum programı bizden “root” isimli yetkili kullanıcının parolasını istenecektir. Bu parola yetkili olarak VTYS'ye bağlanmak için gerekir. Server programın yanı sıra bir yönetim ekranı elde etmek için ayrıca "MySql Workbench" programı da kurulabilir. MySQL Linux sistemlerinde Debian paket yöneticisi ile aşağıdaki gibi basit bir biçimde kurulabilir: $ sudo apt-get install mysql-server Kütüphane dosyaları da şöyle indirilebilir: $ sudo apt-get install libmysqlclient21 MySQL Workbench ise komut satırı yerine Web Sayfasından indirilerek kurululabilir. Yukarıda da belirttiğimiz gibi MySQL kısmen paralı hale getirilince bunun MariaDB isimli bir klonu oluşturuldu. MariaDB'nin uzun vadede açık kaynak kod güvencesi olduğu için tercih edebilirsiniz. >> "SQL" : SQL paralı bir üründür. Fakat bunun da "Express Edition" isminde bedava bir sürümü vardır. Bu sürüm Microsoft'un sayfasından indirilip kurulabilir. Tıpkı MySQL'de olduğu gibi SQL Server'da da yönetim konsol programı vardır. Buna "SQL Server Management Studio" denilmektedir. Bunun da indirilip kurulması tavsiye edilir. >> "SQLite" : SQLite zaten tek bir DLL'den oluşmaktadır. Dolayısıyla aslında kurulumu diye bir durum söz konusu değildir. Fakat biz burada C için örnekler yaparken SQLite başlık dosyalarına ve SQLite DLL’inin import kütüphanesine sahip olmak zorundayız. Bunların nasıl elde edileceği sonraki konularda ele alınacaktır. SQLite yönetim konsolu olarak pek çok alternatif vardır. Bunlardan biri "FireFox Add On" olarak çalışmaktadır. Diğer seçenekler ise “SQLite Studio” ve "SQLite Browser" programlarıdır. Cross Platform olan bu araç ilgili web sayfasından indirilerek kurulabilir. Ya da daha genel "DBeaver" da tercih edilebilir. SQLite'ı Windows için aşağıdaki bağlantıdan indirebilirsiniz: https://www.sqlite.org/download.html Buradan indirilen zip dosyasının içerisinde bir tane ".DLL" dosyası bir ".DEF" dosyası bulunacaktır. Bu DLL'i PATH dizinlerinin içerisine ya da uygulama dizininin içerisine çekebilirsiniz. Linux'ta SQLite şöyle indirilebilir: $ sudo apt-get install sqlite İlişkisel veritabanları tablolardan, tablolar da sütunlardan (fields) oluşmaktadır. Tabii sütunların da veri türleri vardır. SQL Standartları'nda standart bazı veri türleri belirtilmiştir. Ancak SQL VTYS'den VTYS'ye değişiklik gösterebilmektedir. Dolayısıyla her VTYS'nin SQL komutlarında bazı farklılıklar bulunabilmektedir. Biz burada bazı standart sütun türleri üzerinde duracağız. Çalıştığınız VTYS'nin dokümanlarından onlara özgü ayrıntıları elde edebilirsiniz. -> INTEGER: Tamsayısal bilgileri tutan bir türdür. İstenirse kaç digitlik sayıların tutulacağı da belirtilebilir. -> INT: Tipik olarak 4 byte uzunluğunda işaretli tamsayı türüdür. (Örneğin bu tür C’deki int türü ile temsil edilebilir.) -> SMALLINT: Tipik olarak 2 byte'lık işaretli tamsayı türüdür. (Örneğin bu tür C’deki short türü ile temsil edilebilir.) -> BIGINT: Tipik olarak 8 byte uzunluğunda işaretli tamsayı türüdür. (Örneğin bu tür C’deki long long türü ile temsil edilebilir.) -> FLOAT: Tipik olarak 4 byte'lık gerçek sayı türüdür. (Örneğin bu tür C’deki float türü ile temsil edilebilir.) -> DOUBLE: Tipik olarak 8 byte'lık gerçek sayı türüdür. (Örneğin bu tür C’deki double türü ile temsil edilebilir.) -> TIME: Zaman bilgisini saklamak için kullanılan türdür. -> DATE: Tarih bilgisini saklamak için kullanılan türdür. -> CHAR(n): n karakterli yazıyı tutmak için kullanılan türdür. -> VARCHAR(n): En fazla n karakterli bir yazıyı tutmak için kullanılan türdür. -> TINYTEXT: Yazısal bilgileri tutmak için kullanılan türdür. (Tipik olarak 256 byte'a kadar) -> TEXT: Yazısal bilgileri tutmak için kullanılan türdür. (Tipik olarak 64K'ya kadar) -> LONGTEXT: (Tipik olarak 4GB'ye byte'a kadar) -> TINYBLOB: Binary bilgileri tutmak için kullanılan türdür. (Tipik olarak 256 byte'a kadar) -> BLOB: Binary bilgileri tutmak için kullanılan türdür. (Tipik olarak 64K'ya kadar) -> LONGBLOB: Binary bilgileri tutmak için kullanılan türdür. (Tipik olarak 4GB'ye byte'a kadar) Tablo sütunlarının türleri tablo yaratılırken belirlenmektedir. Şimdi de temel SQL komutlarını görelim. SQL bazı ayrıntıları olan dekleratif bir programlama dilidir. Komutlardan oluşmaktadır. Biz burada temel SQL komutlarını ayrıntılarına girmeden ele alacağız. SQL büyük harf küçük harf duyarlılığı olmayan (case insensitive) bir dildir. Ancak geleneksel olarak anahtar sözcüklerin büyük harflerle yazılması tercih edilmektedir. SQL komutlarının sonunda sonlandırıcı olarak ';' karakteri bulundurulmaktadır. Komutlar, -> CREATE DATABASE Komutu: İlişkisel veritabanlarında “veritabanı” tablolardan oluşmaktadır. Bu nedenle önce bir veritabanının yaratılması, sonra da onun içerisinde tabloların yaratılması gerekir. Veritabanlarını yaratmak için CREATE DATABASE komutu kullanılır. Komutun genel biçimi şöyledir: CREATE DATABASE ; Örneğin: CREATE DATABASE student; -> USE Komutu: Belli bir veritabanı üzerinde işlemler yapmak için öncelikle onun seçilmesi gerekir. Bu işlem USE komutuyla yapılır. Komutun genel biçimi şöyledir: USE ; -> SHOW DATABASES Komutu: Bu komut VTYS'de yaratılmış olarak bulunan veritabanlarını gösterir. Komutun genel biçimi şöyledir: SHOW DATABASES; -> CREATE TABLE Komutu: Bu komut veritabanı için bir tablo yaratmak amacıyla kullanılır. Komutun genel biçimi şöyledir: CREATE TABLE ( , , ... ); Aslında bu komutun bazı ayrıntıları vardır. Bu ayrıntılar ilgili dokümanlardan öğrenilebilir. Örneğin: CREATE TABLE student_info(student_id PRIMARY KEY AUTO_INCREMENT, student_name VARCHAR(45), student_no INTEGER); Bir tabloda tekrarlanması yasaklanmış olan sütunlara “birincil anahtar (primary key)” denilmektedir. Tablodaki kayıtların hepsinin birincil anahtar sütunları farklı olmak zorundadır. Başka bir deyişle biz bir tabloya orada zaten var olan birincil anahtar değerine ilişkin bir kayıt ekleyemeyiz. Her tabloda bir tane birincil anahtarın olması tavsiye edilmektedir. Birincil anahtarın tablo yaratılırken CREATE TABLE komutunda belirtilme biçimi çeşitli VTYS’lerde farklı olabilmektedir. -> DROP TABLE Komutu: Bu komut tabloyu silmek için kullanılır. Komutun genel biçimi şöyledir: DROP TABLE ; Örneğin: DROP TABLE person; -> INSERT INTO Komutu: Bu komut bir tabloya bir satır eklemek için kullanılır. Komutun genel biçimi şöyledir: INSERT INTO (sütun1, sütun2, sütun3,...) VALUES (değer1, değer2, değer3,...); Tabloya satır eklerken aslında her sütun bilgisinin belirtilmesi gerekmez. Bu durumda o sütun için tablo yaratılırken (CREATE TABLE komutunda) belirlenmiş olan default değerler kullanılır. Komutun ayrıntılı genel biçimi için ilgili dokümanlara başvurabilirsiniz. Örneğin: INSERT INTO student_info(student_name, student_no) VALUES('Güray Sönmez', 754); Değerler girilirken yazılar ve tarihler tek tırnak içerisinde belirtilmelidir. -> WHERE Cümleciği: Pek çok komut bir WHERE kısmı içermektedir. Where cümleciği koşul belirtmek için kullanılır. Koşullar karşılaştırma operatörleriyle oluşturulur. Mantıksal operatörlerle birleştirilebilir. Örneğin: WHERE age > 20 AND birth_place = 'Eskişehir' LIKE operatörü joker karakterleri kullanılarak belli bir kalıba uyan yazı koşulu oluşturur. Örneğin: WHERE student_name LIKE 'A%' Burada student_name için 'a' ile başlayanlar koşulu verilmiştir. % karakteri "geri kalanı herhangi biçimde olabilir" anlamına gelir. Örneğin: WHERE student_name LIKE '%an' Burada sonu 'an' ile bitenler koşulu verilmiştir. WHERE cümleciğinin bazı detayları vardır. Bu detaylar ilgili dokümanlardan öğrenilebilir. -> DELETE FROM Komutu: Bu komut bir tablodan satır silmek için kullanılır. Komutun genel biçimi şöyledir: DELETE FROM ; -> UPDATE Komutu: Update komutu belli kayıtların alan bilgilerini değiştirmek amacıyla kullanılır. Örneğin ismi "Kağan" olan bir kaydı "Kaan" olarak değiştirmek isteyebiliriz. Ya da bir müşterinin bakiyesini değiştirmek isteyebiliriz. Komutun genel biçimi şöyledir: UPDATE SET alan1 = değer1, alan2 = değer2, ... WHERE ; Örneğin: UPDATE student_info SET student_name = 'Kaan Kaplan' WHERE student_name = 'Kaan Aslan' DELETE ve UPDATE komutlarını kullanrıken dikkat ediniz. Çünkü eğer koşul belirtmezseniz ya da koşulu yanlış belirtirseniz yaptıpınız işlemden birden fazla kayıt etkilenir. Örneğin: UPDATE student_info SET student_name = 'Ali Ballı' WHERE student_name = 'Veli Ballı'; Burada koşul zayıf oluşturulmuştur. Bu durumda bütün "Veli Ballı" isimleri "Ali Ballı" olarak değiştirilir. Örneğin: UPDATE student_info SET student_name = 'Ali Ballı' WHERE student_name = 'Veli Ballı' AND student_no = 754; Artık burada ismi "Veli Ballı" olan ve numarası 754 olan satırın ismi "Ali Ballı" olarak değiştirilecektir. -> SELECT Komutu: Koşulu sağlayan kayıtların elde edilmesi SELECT komutuyla yapılmaktadır. SELECT komutunun genel biçimi oldukça ayrıntılıdır. Çünkü komuta çeşitli cümlecikler monte edilebilmektedir. Komutun genel biçimi şöyledir: SELECT FROM [WHERE 600; Burada öğrenci numarası 600'den büyük olan öğrencilerin tüm sütun bilgileri elde edilmiştir. Eğer SELECT edilen kayıtlar belli bir sütuna göre sıralı biçimde elde edilmek istenirse ORDER BY cümleciği komuta eklenir. Örneğin: SELECT * FROM student WHERE student_id > 600 ORDER BY stdent_name; Burada öğrenci numarası 600'den büyük olan öğrencilerin tüm sütun bilgileri elde edilmiştir. Eğer SELECT edilen kayıtlar belli bir sütuna göre sıralı biçimde elde edilmek istenirse ORDER BY cümleciği komuta eklenir. Örneğin: SELECT * FROM student WHERE student_id > 600 ORDER BY stdent_name; ORDER BY default olarak kayıtları küçükten büyüğe (ASC) vermektedir. Ancak DESC ile büyükten küçüğe de sıralama yapılabilir. Örneğin: SELECT * FROM student WHERE student_no > 600 ORDER BY student_name DESC; ORDER BY cümleciğinde birden fazla sütun belirtilebilir. Bu durumda ilk sütun değerleri aynıysa diğer sütunlar dikkate alınır. Örneğin: SELECT * FROM student WHERE student_no > 600 ORDER BY student_name DESC, student_no ASC; Burada ismi aynı olanlar numaralarına göre küçükten büyüğe elde edilecektir. LIMIT cümleceği de SELECT cümlesiyle kullanılabilir. LIMIT anahtar sözcüğnün yanında bir sayı bulunur. Koşulu sağlayan kayıtların belli sayıda miktarını elde etmek için kullanılır. Örneğin: SELECT * FROM student WHERE student_no > 600 ORDER BY student_name DESC, student_no ASC LIMIT 10; WHERE cümleciğinde built-in fonksiyonlar kullanılabilir. Örneğin: SELECT * FROM city WHERE char_length(city) = 6; şeklindedir. İlişkisel veritabanlarında tablolarda veri tekrarı istenmez. Örneğin bir öğrenci veritabanı oluşturacak olalım. Bir öğrencinin çeşitli bilgilerinin yanı sıra onun okulu hakkında da bilgileri tutmak isteyelim. Aşağıdaki gibi bir tablo tasarımı uygun değildir: Adı Soyadı No Okul Adı Okulun Bulunduğu Şehir Okulun Türü -------------------------------------------------------------------------------------- Ali Serçe 123 Tarsus Amerikan Lisesi Mersin Devlet Lisesi Kaan Aslan 745 Eskişehir Atatürk Lisesi Eskişehir Devlet Lisesi Hasan Bulur 734 Tarsus Amerikan Lisesi Mersin Devlet Lisesi ... ... ... ... Burada Okul Adı bilgileri gereksiz bir biçimde tekrarlanmaktadır. Bu tekrarı engellemek için iki tablo oluşturabiliriz. Öğrenci Tablosu Adı Soyadı No Okul ID'si ---------------------------------- Ali Serçe 123 100 Kaan Aslan 745 235 Hasan Bulur 734 100 ... ... ... Okul Tablosu Okul Id'si Okul Adı Okulun Bulunduğu Şehir Okulun Türü ------------------------------------------------------------------------------------ ... ... ... ... 100 Tarsus Amerikan Lisesi Mersin Devlet Lisesi 150 Eskişehir Atatürk Lisesi Eskişehir Devlet Lisesi ... ... ... ... Burada veri tekrarı ortadan kaldırılmıştır. Tabii bu tablolarda da Okul ID'si ortak bir sütundur. Bu ortak sütun tablolar arasında ilişki kurmak için gerekmektedir. Bu tür sütunlara "foreign key" de denilmektedir. Ancak yukarıdaki gibi tekrarlar engellendiğinde gerekli bilgiler artık tek bir tablodan değil, çeşitli tablolardan çekilip alınacaktır. İşe çeşitli tablolardan bilgilerin çekilip alınması işlemine "JOIN" işlemi denilmektedir. JOIN işleminin birkaç biçimi vardır (INNER JOIN, OUTER JOIN, LEFT JOIN, RIGHT JOIN gibi). Ancak en fazla kullanılan JOIN işlemi "INNER JOIN" denilen işlemdir. JOIN denildiğinde zaten default olarak INNER JOIN anlaşılır. INNER JOIN işleminde eğer iki tablo söz konusu ise önce iki tablonun kartezyen çarpımları elde edilir. Her kaztezyen çarpım iki tablonun birleştirilmesi biçiminde ("join" ismi oradan geliyor) elde edilmektedir. Sonra kartezyen çarpımlarda yalnızca belli koşulu sağlayan satırlar elde edilir. Böylece tablolar "ilişkisel (relational)" biçimde birleştirilmiş olur. INNER JOIN sentaksı iki biçimde oluşturulabilmektedir. Birinci sentaks klasik eski tip sentakstır. İkinci sentaks daha modern biçimdir. Klasik eski tip sentaks şöyledir: SELECT FROM INNER JOIN ON ; Örneğin: SELECT student.student_name, student.student_no, school.school_name FROM student INNER JOIN school ON student.school_id = school.school_id WHERE stduent.student_no > 600; Sütun isimleri belirtilirken eğer çakışma yoksa yalnızca isimler yazılabilir. Ancak çakışma varsa tablo ismi ve nokta operatörü ile sütunun hangi tabloya ilişkin olduğu belirtilmelidir. Bazı uygulamacılar çakışma olsa da olmasa da niteliklendirme yaparlar. Bazı uygulamacılar yalnızca çakışan sütunlarda niteliklendirme yaparlar. Yukarıdaki örnekte tüm sütunlar niteliklendirilerek belirtilmiştir. Bu örnek şöyle de yapılabilirdi: SELECT student_name, student_no, school_name FROM student INNER JOIN school ON student.school_id = school.school_id WHERE student_no > 600; Modern INNER JOIN sentaksında SELECT komutunun FROM kısmında birden fazla tablo ismi belirtilir. Koşul da yine WHERE cümleciğine taşınır. Örneğin: SELECT student_name, student_no, school_name FROM student, school WHERE student.school_id = school.school_id AND student_no > 600; Daha çok bu modern biçim tercih edilmektedir. Şimdi de SQLite, MYSQL gibi VTYS'leri nasıl kullanacağımıza değinelim: >> "SQLite" : İşlemlere başlamadan önce SQLite’ın programalama kurulumunu yapmamız gerekir. SQLite yukarıda da belirtitğimiz gibi çok küçük (tek bir dinamik kütüphaneden oluşan) bir VTYS’dir. Dolayısıyla onun kurulması Windows’ta bildiğimiz anlamda bir setup işlemi ile yapılmaz. Tabii bizim C’den SQLite kütüphanesini kullanabilmemiz için ona ilişkin başlık ve kütüphane dosyalarını elde etmemiz gerekir. Windows'ta SQLite'ın resmi indirme sitesi şöyledir: https://sqlite.org/download.html Buradan aşağıdaki iki indirme yapılır: -> Precompiled Binaries for Windows (32 bit ya da 64 bit) -> SQLite Amalgamation Birinci indirmede tek bir DLL elde edilecektir. İkinci indirmede de "sqlite3.h" başlık dosyası ve kaynak dosyası elde edilecektir. Ayrıca SQlite için "sqlite3" isminde komut satırından kullanılan bir program da bulundurulmuştur. Bu programın kullanımına ilişkin bilgileri aşağıdaki bağlantıdan edinebilirsiniz: https://www.sqlite.org/cli.html Birinci indirmede Windows için gereken sqlite3.dll ve sqlite3.def dosyaları elde edilir. Buradaki “.def” dosyasına “module definition file” denilmektedir. Bu dosya “DLL’in import kütüphanesi” gibi link aşamasına dahil edilebilir. Ya da istenirse aşağıdaki komutla bu “.def” dosyasından “.lib” uzantılı “import kütüphanesi de oluşturulabilmektedir: LIB /DEF:sqlite3.def /machine:x86 Buradaki machine argümanı hedef sistemi belirtmektedir. Burada 32 bit Windows sistemleri için x86, 64 bit Windows sistemleri için "x64" kullanılmalıdır. Örneğin: LIB /DEF:sqlite3.def /machine:x64 İkinci indirmeden biz SQLite’ın kaynak dosyalarını elde ederiz. Buradaki “sqlite3.h” dosyası SQLite fonksiyonları için başlık dosyası niteliğindedir. Mac OS X için kurulum Windows’takine benzemektedir. Yine ilgili “.zip” dosyaları indirilip kurulum yapılabilir. Bu sistemlerde derleme yaparken link aşamasında "-lsqlite3" ile kütüphane dosyasını belirtmeyi unutmayınız. Kurulum sonrası her şeyin hazır oladuğunu anlamak için SQLite kütüphanesinin versiyon numarasını yazdıran aşağıdaki gibi bir programla test işlemi yapabilirsiniz: #include #include "sqlite3.h" int main(void) { printf("%s\n", sqlite3_libversion()); return 0; } C’de SQLite veritabanı ile işlem yapmak için önce o veritabanının sqlite3_open fonksiyonuyla açılması gerekir. Bu işlemden sqlite3 türünden bir handle elde edilir. sqlite3_open fonksiyonunun prototipi şöyledir: int sqlite3_open( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); Fonksiyonun birinci parametresi bizden sqlite dosyasının yol ifadesini alır. İkinci parametresi sqlite3 isimli yapı türünden bir göstricinin adresini almaktadır. Fonksiyon handle alanını (yani sqlite yapı nesnesini) oluşturur. Onun adresini bu göstericinin içerisine yerleştirir. Fonksiyonun geri dönüş değeri işlemin başarısını belirtmektedir. Fonksiyon başarılıysa SQLITE_OK değerine geri döner. Fonksiyon başarısız olduğunda yine dosyanın sqlite3_close fonksiyonuyla kapatılması gerekir. Hata nedeni de sqlite3_errmsg fonksiyonuyla yazdırılabilir. Bu durumda sqlite dosyasının açılması tipik olarak şöyle yapılabilir: if (sqlite3_open("student.db", &db) != SQLITE_OK) { fprintf(stderr, "sqlite3_open failed: %s\n", sqlite3_errmsg(db)); sqlite3_close(db); exit(EXIT_FAILURE); } sqlite3_open fonksiyonu dosya varsa olanı açar, yoksa yeni bir SQLite DB dosyası yaratır. sqlite3_errmsg fonksiyonunun parametrik yapısı şöyledir: const char *sqlite3_errmsg(sqlite3 *db); sqlite3_close fonksiyonunun prototipi ise şöyledir: int sqlite3_close(sqlite3*); Dosya kapatılırken başarı kontrolü yapmaya gerek yoktur. Başarısızlık durumlarında hata mesajını stderr dosyasına yazdırıp programı sonlandıran bir sarma fonksiyon şöyle yazılabilir: void sqlite3_exit(const char *msg, sqlite3 *db) { fprintf(stderr, "%s failed => %s\n", msg, sqlite3_errmsg(db)); sqlite3_close(db); exit(EXIT_FAILURE); } Böylece biz hata durumlarını aşağıdaki gibi ele alabiliriz: if (sqlite3_open("studentxxx.db", &db) != SQLITE_OK) sqlite3_exit("sqlite3_open", db); Aşağıda bir örnek program verilmiştir: * Örnek 1, #include #include #include "sqlite3.h" void sqlite3_exit(const char *msg, sqlite3 *db); int main(void) { sqlite3 *db; if (sqlite3_open("student.db", &db) != SQLITE_OK) sqlite3_exit("sqlite3_open", db); printf("success...\n"); sqlite3_close(db); return 0; } void sqlite3_exit(const char *msg, sqlite3 *db) { fprintf(stderr, "%s failed => %s\n", msg, sqlite3_errmsg(db)); sqlite3_close(db); exit(EXIT_FAILURE); } SQLite'a bir SQL cümlesi göndermek için sqlite3_exec fonksiyonu kullanılmaktadır. Fonksiyonun prototipi şöyledir: int sqlite3_exec( sqlite3 *db, /* An open database */ const char *sql, /* SQL to be evaluated */ int (*callback)(void*,int,char**,char**), /* Callback function */ void *param, /* 1st argument to callback */ char **errmsg /* Error msg written here */ ); Fonskiyonun birinci parametresi sqlite3_open fonksiyonundan elde edilen handle değeridir. İkinci parametre sql cümlesinin yazısını alır. Üçüncü parametre işlemden sonra çağrılacak “callback” fonksiyonun adresini almaktadır. Bu parametre NULL geçilebilir. Dördüncü parametre bu “callback” fonksiyona geçirilecek argümanı belirtir. Bu parametre de NULL geçilebilir. Son parametre ise char * türünden bir göstericinin adresini almaktadır. Hata drumunda hata mesajının adresi bu göstericiye yerleştirilir. Bu parametre NULL olarak da geçilebilir. Fonksiyonun geri dönüş değeri işlemin başarısını belirtir. Fonksiyon başarılıysa SQLITE_OK değerine geri dönmektedir. Bu durumda biz hata mesajını yazdırdıktan sonra sqlite3_free fonksiyonu ile tahsis edilen alanı serbest bırakabiliriz. Örneğin: if (sqlite3_exec(db, "INSERT INTO student_info(student_name, student_no) VALUES('Rasim Öztekin', 367)", NULL, NULL, NULL) != SQLITE_OK) sqlite3_exit("sqlite3_exec", db); Aşağıdaki örnekte student_info veri tabanına sqlite3_exec fonksiyonu ile bir kayıt eklenmiştir. * Örnek 1, #include #include #include "sqlite3.h" void sqlite3_exit(const char *msg, sqlite3 *db); int main(void) { sqlite3 *db; if (sqlite3_open("student.db", &db) != SQLITE_OK) sqlite3_exit("sqlite3_open", db); if (sqlite3_exec(db, "INSERT INTO student_info(student_name, student_no) VALUES('Rasim Öztekin', 367)", NULL, NULL, NULL) != SQLITE_OK) sqlite3_exit("sqlite3_exec", db); printf("Success...\n"); sqlite3_close(db); return 0; } void sqlite3_exit(const char *msg, sqlite3 *db) { fprintf(stderr, "%s failed => %s\n", msg, sqlite3_errmsg(db)); sqlite3_close(db); exit(EXIT_FAILURE); } Veritabanından kayıtların elde edilmesi biraz daha ayrıntılı bir konudur. İstenilen kayıtların elde edilmesi için iki yol vardır. Birincisinde önce sqlite3_prepare fonksiyonu ile SQL SELECT cümlesi VTYS’ye gönderilir. Sonra her bir kayıt tek tek sqlite3_step fonksiyonu çağrılarak elde edilir. sqlite3_prepare fonksiyonundan elde edilen kayıtların bir liste oluşturduğunu sqlite3_step fonksiyonunun da listede sonraki kayıta geçtiğini düşünebilirsiniz. Yani adeta sqlite3_step fonksiyonu imleci bir sonraki kayda konumlandırıyormuş gibidir. O andaki kayıtın sütun elemanları sqlite3_column_xxx fonksiyonlarıyla elde edilebilir. Burada xxx o sütunun türünü belirtmektedir. sqlite3_prepare fonksiyonunun prototipi şöyledir: int sqlite3_prepare( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); Fonksiyonun birinci parametresi sqlite3_open fonksiyonundan elde edilen handle değeridir. İkinci parametre SELECT cümlesini belirtir. Üçüncü parametre ikinci parametredeki SELECT cümlesine ilişkin yazının uzunluğunu belirtir. Bu parametre negatif değer geçilirse (örneğin -1) bu yazı null karaktere kadar ele alınır. Fonksiyonun dördüncü parametresi sqlite3_stmt türünden bir yapı göstericisinin adresini almaktadır. Bu da bir handle değeri gibidir. Kayıtlar elde edilirken bu handle değeri kullanılmaktadır. Son parametre NULL geçilebilir. Fonksiyon başarı durumunda SQLITE_OK değerine geri dönmektedir. Fonksiyon başarılı olduğunda imleç ilk kayıdın bir gerisini göstermektedir. Yani işleme önce bir kez sqlite3_step çağrısı yaparak başlamak gerekir. Her sqlite3_step çağrısı select edilen kayıtlardan bir sonrasına konumlanma sağlar. sqlite3_step fonksiyonunun prototipi şöyledir: int sqlite3_step(sqlite3_stmt*); Fonksiyonun parametresi sqlite3_prepare fonksiyonundan alınan handle değeridir. Son kayda erişildikten sonra sqlite3_step fonksiyonu SQLITE_DONE değerine geri dönmektedir. O halde bu yöntemde önce bir kez sqlite3_prepare fonksiyonu çağrılır. Sonra bir döngü içerisinde sqlite2_step çağrıları yapılır. İmleç konumlandırıldıktan sonra sütun değerleri sqlite3_column_xxx fonksiyonlarıyla elde edilmektedir. Her sütun türü için ayrı bir fonksiyon vardır. Bu fonksiyonlardan bazılarının prototipleri aşağıda verilmiştir: const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); int sqlite3_column_bytes(sqlite3_stmt*, int iCol); int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); double sqlite3_column_double(sqlite3_stmt*, int iCol); int sqlite3_column_int(sqlite3_stmt*, int iCol); sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); const void *sqlite3_column_text16(sqlite3_stmt*, int iCol); int sqlite3_column_type(sqlite3_stmt*, int iCol); sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); Bu işlemler bittikten sonra sqlite3_finalize fonksiyonu çağrılmalıdır. Fonksiyonun prototipi şöyledir: int sqlite3_finalize(sqlite3_stmt *pStmt); Fonksiyon başarı durumunda SQLITE_OK değeri ile geri dönmektedir. * Örnek 1, Aşağıdaki örnekte komut satırında bir menü çıkartılmış ve seçilen seçeneğe göre uygun işlemler yapılmıştır. Menü aşağıdaki gibidir: 1) Kayıt Ekle 2) Kayıt Sil 3) Kayıt Bul 4) Çıkış Seçiminiz: Örnekteki "student.db" veritabanı yoksa yaratılmaktadır varsa olan veritabanı açılmaktadır. Eğer veritabanı yoksa aşağıdaki SQL komutuyla tablo yaratılmıştır: "CREATE TABLE IF NOT EXISTS student_info(student_id INTEGER PRIMARY KEY AUTOINCREMENT, student_name VARCHAR(64), student_no INTEGER );" Görüldüğü gibi student_infıo tablosunda üç sütun vardır. Sütunlardan biri otomatik artırımlı PRIMARY KEY sütunudur. #include #include #include #include #include "sqlite3.h" int disp_menu(void); void clear_stdin(void); void add_record(sqlite3 *db); void del_record(sqlite3 *db); void find_record(sqlite3 *db); void sqlite3_exit(const char *msg, sqlite3 *db); int main(void) { sqlite3 *db; int option; setlocale(LC_ALL, "tr_TR.UTF-8"); if (sqlite3_open("student.db", &db) != SQLITE_OK) sqlite3_exit("sqlite3_open", db); if (sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS student_info(" "student_id INTEGER PRIMARY KEY AUTOINCREMENT, " "student_name VARCHAR(64), " "student_no INTEGER);", NULL, NULL, NULL != SQLITE_OK)) sqlite3_exit("sqlite3_exec", db); for (;;) { if ((option = disp_menu()) == -1) { printf("Geçersiz seçenek!..\n"); clear_stdin(); continue; } switch (option) { case 1: add_record(db); break; case 2: del_record(db); break; case 3: find_record(db); break; case 4: goto EXIT; default: printf("Geçersiz seçenek!..\n"); } } EXIT: sqlite3_close(db); return 0; } int disp_menu(void) { int option; printf("1) Kayıt Ekle\n"); printf("2) Kayıt Sil\n"); printf("3) Kayıt Bul\n"); printf("4) Çıkış\n"); printf("Seçiminiz: "); fflush(stdout); if (scanf("%d", &option) != 1) return -1; clear_stdin(); return option; } void clear_stdin(void) { while (getchar() != '\n') ; } void add_record(sqlite3 *db) { char name[64]; char sql[1024]; char *str; int no; printf("Adı Soyadı:"); fflush(stdout); fgets(name, 64, stdin); if ((str = strchr(name, '\n')) != NULL) *str = '\0'; printf("No:"); fflush(stdout); scanf("%d", &no); clear_stdin(); sprintf(sql, "INSERT INTO student_info(student_name, student_no) VALUES('%s', %d);", name, no); if (sqlite3_exec(db, sql, NULL, NULL, NULL) != SQLITE_OK) fprintf(stderr, "cannot add record!..\n"); } void del_record(sqlite3 *db) { char condition[1024]; char sql[4096]; char *str; int no; printf("Koşul:"); fflush(stdout); fgets(condition, 1024, stdin); if ((str = strchr(condition, '\n')) != NULL) *str = '\0'; sprintf(sql, "DELETE FROM student_info WHERE %s;", condition); if (sqlite3_exec(db, sql, NULL, NULL, NULL) != SQLITE_OK) fprintf(stderr, "cannot delete record!..\n"); } void find_record(sqlite3 *db) { char condition[1024]; char sql[4096]; char *str; int no; sqlite3_stmt *stmt; unsigned char *name; printf("Koşul:"); fflush(stdout); fgets(condition, 1024, stdin); if ((str = strchr(condition, '\n')) != NULL) *str = '\0'; printf("\n"); if (*str == '\0') strcpy(sql, "SELECT student_name, student_no FROM student_info"); else sprintf(sql, "SELECT student_name, student_no FROM student_info WHERE %s;", condition); if (sqlite3_prepare(db, sql, -1, &stmt, NULL) != SQLITE_OK) { fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db)); return; } while (sqlite3_step(stmt) != SQLITE_DONE) { name = sqlite3_column_text(stmt, 0); no = sqlite3_column_int(stmt, 1); printf("%s, %d\n", name, no); } sqlite3_finalize(stmt); printf("\n"); } void sqlite3_exit(const char *msg, sqlite3 *db) { fprintf(stderr, "%s failed => %s\n", msg, sqlite3_errmsg(db)); sqlite3_close(db); exit(EXIT_FAILURE); } >> "MySQL" : Şimdi de yukarıdaki işlemlerin benzerlerinin MySQL'de nasıl yapılacağını görelim. MySQL'in de C için API'leri vardır. MySQL VTYS’si ile işlemler MySQL’in gerekli kütüphanelerinin client tarafta kurulması gerekir. Bunun için Windows’ta, macOS sistemlerinde ve Linux’ta “MySQL C++ Connector” denilen kurulum yapılabilir. Bu paket aşağıdaki bağlantıdan indirilebilir: https://downloads.mysql.com/archives/c-c/ Bu kurulum yapıldığında tipik olarak gerekli olan kütüphaneler ve include dosyaları "C:\Program Files\MySQL\MySQL Connector C 6.1" gibi bir dizine kurulacaktır. Debian türevi (Ubuntu, Mint vs.) sistemlerde aşağıdaki apt-get komutu bu paketin indirilerek kurulmasını sağlamaktadır: $ sudo apt-get install libmysqlclient-dev Windows ortamında VisualStudio IDE'sinde çalışıyorsanız projenizde "Additional Include Drectories" elemanında MySQL Connector'ü kurduğunuz dizindeki include dizinini burada belirtmelisiniz. Ayrıca Windows'ta link işlemi için "libmysql.lib" import kütüphanesinin de projede belirtilmesi gerekmektedir. Ancak programın çalışabilmesi için "libmysql.dll" dosyasının ya sistem tarafından bakılan dizinlerin birinde olması ya da PATH çevre değişkeni ile belirtilen dizinlerden birinde olması gerekmektedir. Linux'ta MySQL include dosyaları "/usr/include/mysql" dizini içerisindedir. Dolayısıyla include işlemi biçiminde yapılmalıdır. Linux'ta ayrıca "libmysqlclient" kütüphanesinin de bağlama işlemine dahil edilmesi gerekmektedir. Derleme işlemini şöyle yapmalısınız: $ gcc -o sample sample.c -lmysqlclient Bir MYSQL C programda ilk yapılacak şey mysql_init fonksiyonu çağırarak bir handle elde etmektir: MYSQL *MySQL_init(MYSQL *MySQL); Bu fonksiyon parametre olarak bizden MYSQL türünden bir nesnenin adresini ister onun içini doldurur. Eğer parametre NULL girilirse fonksiyon bu nesneyi kendisi tahsis edip bize aresini verecektir. Fonksiyon başarısız olabilir. Başarısızlık durumunda NULL adrese geri döner. Örneğin: MYSQL *db; if ((db = mysql_init(NULL)) == NULL) { fprintf(stderr, "MySQL_init failed\n"); exit(EXIT_FAILURE); } Aşağıda örnek bir kullanım verilmiştir: * Örnek 1, #include #include #include int main(void) { MYSQL *db; if ((db = mysql_init(NULL)) == NULL) { fprintf(stderr, "mysqlL_init failed\n"); exit(EXIT_FAILURE); } printf("Ok\n"); return 0; } MYSQL * türünden handle elde ediltiktan sonra artık "IP adresi", "port numarası", "kullanıcı adı", "parola" ve bağlanılacak veritabını velirtilerek bağlantı mysl_real_connect fonksiyonuyla sağlanır. Fonksiyonun prototipi şöyledir: MYSQL *mysql_real_connect(MYSQL *MySQL, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long client_flag); Fonksiyonun birinci parametresi mysql_init fonksiyonundan elde edilmiş olan handle değeridir. İkinci parametre host'un IP adresini üçüncü parametre MySQL'deki kullanıcı ismini almaktadır. MySQL'i ilk kurduğunuzda tüm yetkilere sahip bir "root" kullanıcısı bulunmaktadır. Bu kullanıcı tüm veritabanlarına erişebilmektedir. Tabii siz isterseniz kısıtlı kullanılarda yaratabilirsiniz. Fonksiyonun dördüncü parametresi kullanıcıya ilişkin parolayı belirtmektedir. "root" kullanıcısının parolası kurulum sırasında belirlenmektedir. Beşinci parametre kullanılacak veritabaınının ismini almaktadır. Altıncı parametre port numarasını belirtmektedir. MySQL Server programlarının kullandığı default port numarası 3306'dır. Son iki parametre NULL ve 0 biçiminde geçilebilir. Örneğin: MYSQL *db; if ((db = mysql_init(NULL)) == NULL) { fprintf(stderr, "mysql_init failed!..\n"); exit(EXIT_FAILURE); } if (mysql_real_connect(db, "localhost", "root", "maviay", "student_info", 3306, NULL, 0) == NULL) exit_err("mysql_real_connect failed", NULL); MySQL ile çlışırken hata durumlarında bağlantıyı kapatıp prosesi sonlandırmak için aşağıdaki gibi bir fonksiyondan faydalanabiliriz: void exit_err(const char *msg, MYSQL *db) { fprintf(stderr, "%s: %s\n", msg, mysql_error(db)); mysql_close(db); exit(EXIT_FAILURE); } mysql_close fonksiyonuna NULL adres geçilirse close işlemi yapılmaz ama mysql_init fonkisyonuyla tahsis edilmiş olan handle alanu boşaltılır. Server'a bağlanırken birkaç problem ortaya çıkabilir. Server default SSL kullanıyor olabilir. Bu durumda SSL konfigürasyonunu yapmadıysanız bağlantı sırasında sorun oluşabilir. Bağlanırken SSL'i pasif hale getirmek için (disable etmek için) iki yöntem kullanılabilir. Birincisi "mysql.ini" ya da "my.cnf" dosyasına aşağıdaki satırlar girilerek SSL kullanımı devra dışı bırakılabilir: [mysqld] ssl=0 skip_ssl Windows sistemlerinde "mysql.ini" dosyası "C:\ProgramData\MySQL\MySQL Server 8.0" dizin içerisinde bulunmaktadır. Linux sistemlerinde "my.cnf" dosyası "/etc/mysql" dizini içerisindedir. Bu dosyaları edit etmek için editörünüzü "Administrator" ya da "sudo" hakkıya açmalısınız. İkinci yöntem programa özgü bir biçimde SSL'i mysql_options fonksiyonu ile devre dışı bırakmaktır. Bu işlem şöyle yapıabilir: int ssl_mode = SSL_MODE_DISABLED; if (mysql_options(db, MYSQL_OPT_SSL_MODE, &ssl_mode)) exit_err("mysql_options failed", db); Eskiden MySQL server programı default olarak uzak bağlantıları kabul ediyordu. Sonra default durumda uzak bağlantılara izin verilmemeye başlandı. Yani siz yerel ağınızda bile olsa başka bir makinedeki MySQL Server programına IP adresi ve port numarası belirterek bağlanamayabilirsiniz. Uzak bağlantılara izin vermek için konfügrasyonda bazı ayarlamaların yapılması gerekmektedir. SQL cümlesini server’a gönderip işletmek için MySQL_query fonksiyonu kullanılmaktadır. Fonksiyonun prototipi şöyledir: int mysql_query(MYSQL *MySQL, const char *stmt_str); Fonksiyonun birinci parametresi handle değerini ikinci paarametresi SQL komut yazısını alır. Fonksiyon başarı durumunda sıfır başarısızlık durumunda sıfır dışı bir değere geri döner. Örneğin: if (mysql_query(db, u8"INSERT INTO student_info(student_name, student_no) VALUES('Ali Eker', 345)") != 0) exit_err("mysql_query", db); Aşağıda program bir bütün olarak verilmiştir. * Örnek 1, #include #include #include void exit_err(const char *msg, MYSQL *db); int main(void) { MYSQL *db; int ssl_mode; if ((db = mysql_init(NULL)) == NULL) exit_err("mysql_init failed", NULL); ssl_mode = SSL_MODE_DISABLED; if (mysql_options(db, MYSQL_OPT_SSL_MODE, &ssl_mode)) exit_err("mysql_options failed", db); if (mysql_real_connect(db, "localhost", "root", "kaanaslan1966", "student", 3306, NULL, 0) == NULL) exit_err("mysql_real_connect failed", db); if (mysql_query(db, u8"INSERT INTO student_info(student_name, student_no, student_school_id) VALUES('Ali Eker', 122, 1)") != 0) exit_err("mysql_query", db); printf("Ok\n"); mysql_close(db); return 0; } void exit_err(const char *msg, MYSQL *db) { fprintf(stderr, "%s: %s\n", msg, mysql_error(db)); if (db != NULL) mysql_close(db); exit(EXIT_FAILURE); } MySQL server'ı Linux'ta kullanıyorsanız tüm ayarlar default durumda Unicode UTF-8 enconding'ine ilişkindir. Linux ortamında uzun süredir default encoding zaten Unicode UTF-8 olduğu için bu ortamda çalışırken bir encoding sorunu ortaya çıkmayacaktır. Ancak Windows sistemlerinde MySQL server kurulduğunda default encoding Unicode UTF-8 yerine Microsoft'un 1254 Code Page'i olabilir. MySQL'de encoding çeşitli düzeylerde değiştirilebilmektedir. Örneğin: -> Server Düzeyinde -> Veritabanı Düzeyinde -> Tablo Düzeyinde -> Tablonun Sütunu Düzeyinde Aşağıdaki belirleme yukarıda yapılan belirlemeyi devre dışı bırakmaktadır. Ayrıca MySQL'de client ptogramlar da server ile bağlandığında server'dan belli bir encoding kullanmasını isteyebilmektedir. Yani server ayarları yapılmış olsa bile client programların da encoding isteklerinin uygun olması gerekebilmektedir. Client'ın default encoding davranış da aslında server ayarlarından belilenebilmektedir. Client için bu ayar uygun değile client program bağlantıdan sonra mysql_options fonksiyonu ile bunu aşağıdaki gibi değiştirebilir: if (mysql_options(db, MYSQL_SET_CHARSET_NAME, "utf8") != 0) exit_err("mysql_options_failed", db); Belli koşulu sağlayan kayıtların ele geçirilmesi için başka bir deyişle SELECT cümlesi ile seçilen kayıtların elde edilmesi için birkaç yol vardır. Bunun için önce SELECT cümlesi yine sql_query fonksiyonuyla uygulanır. Sonra select edilen kayıtların elde edilmesi için şu işlemler yaplır: -> mysql_store_result fonksiyonu çağrılarak bir “result handle değeri” elde edilir: MYSQL_RES *mysql_store_result(MYSQL *MySQL); Fonksiyon parametre olarak bizden mysql_init ile elde edilen handle değerini alır ve bize kayıtları elde etmemiz için gereken MYSQL_RES * türünden bir handle değeri verir. Fonksiyon başarısız olursa NULL adrese geri dönmektedir. -> Kayıtların tek tek ele geçirilmesi için mysql_fetch_row fonksiyonu bir döngü içerisinde çağrılır. Bu fonksiyonun prototipi şöyledir: MYSQL_ROW mysql_fetch_row(MYSQL_RES *result); Fonksiyon mysql_store_result fonksiyonundan elde edilen handle değerini alır ve MYSQL_ROW türüyle geri döner. Bu tür aslında char ** biçiminde typedef edilmiştir. Yani char türünden göstericileri tutan dizinin adresini belirtir. İşte bu fonksiyon NULL adres döndürene kadar döngü içerisinde ilerlenir. Arık kayıtlara ilişkin sütun bilgilerine MYSQL_ROW türünden göstericiye sütun numarası indeks yapılarak erişilir. Ancak bu türden erişim bize tüm sütunları yazı gibi vermektedir. Kayıtlar elde ediltikten sonra mysql_store_result ile elde edilen alan mysql_free_result fonksiyonu ile boşaltılmalıdır. Fonksiyonun parametrik yapısı şöyledir: void mysql_free_result(MYSQL_RES *result); Örneğin: if (mysql_query(db, "SELECT student_name, student_no FROM student_info") != 0) exit_err("mysql_query", db); if ((res = mysql_store_result(db)) == NULL) exit_err("mysql_store_result", db); while ((row = mysql_fetch_row(res)) != NULL) printf("%s, %s\n", row[0], row[1]); mysql_free_result(res); Burada önce SELECT cümlesi server'a gönderilmiş, sonra mysql_store_result fonksiyonu ile sonuçlar alınıp satırlar da mysql_fetch_row çağrıları ile tek tek elde edilmiştir. Aşağıda bu konuya ilişkin bir örnek verilmiştir: * Örnek 1, #include #include #include #include void exit_err(const char *msg, MYSQL *db); int main(void) { MYSQL *db; int ssl_mode; MYSQL_RES *res; MYSQL_ROW row; if ((db = mysql_init(NULL)) == NULL) exit_err("mysql_init failed", NULL); if (mysql_options(db, MYSQL_SET_CHARSET_NAME, "utf8") != 0) exit_err("mysql_options_failed", db); ssl_mode = SSL_MODE_DISABLED; if (mysql_options(db, MYSQL_OPT_SSL_MODE, &ssl_mode) != 0) exit_err("mysql_options failed", db); if (mysql_real_connect(db, "localhost", "root", "kaanaslan1966", "student", 3306, NULL, 0) == NULL) exit_err("mysql_real_connect failed", db); if (mysql_query(db, "SELECT student_name, student_no FROM student_info") != 0) exit_err("mysql_query", db); if ((res = mysql_store_result(db)) == NULL) exit_err("mysql_store_result", db); while ((row = mysql_fetch_row(res)) != NULL) printf("%s, %s\n", row[0], row[1]); mysql_free_result(res); mysql_close(db); return 0; } void exit_err(const char *msg, MYSQL *db) { fprintf(stderr, "%s: %s\n", msg, mysql_error(db)); if (db != NULL) mysql_close(db); exit(EXIT_FAILURE); }